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/05 21:00:32 UTC

[09/11] incubator-freemarker git commit: Configuration is now immutable. Instead, you should use Configuration.Builder to set up the setting values, then create the Configuration with the builder's build() method. FreemarkerServlet (including some of its

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/core/ConfigurationException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ConfigurationException.java b/src/main/java/org/apache/freemarker/core/ConfigurationException.java
index 42ab6e1..5b61cca 100644
--- a/src/main/java/org/apache/freemarker/core/ConfigurationException.java
+++ b/src/main/java/org/apache/freemarker/core/ConfigurationException.java
@@ -23,7 +23,7 @@ package org.apache.freemarker.core;
  * Error while configuring FreeMarker.
  */
 @SuppressWarnings("serial")
-public class ConfigurationException extends Exception {
+public class ConfigurationException extends RuntimeException {
 
     public ConfigurationException(String message, Throwable cause) {
         super(message, cause);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java b/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
index c480418..3ed6512 100644
--- a/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
+++ b/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
@@ -18,30 +18,69 @@
  */
 package org.apache.freemarker.core;
 
-import org.apache.freemarker.core.util._NullArgumentException;
+import java.util.Date;
+
+import org.apache.freemarker.core.Configuration.ExtendableBuilder;
 import org.apache.freemarker.core.util._StringUtil;
 
 /**
- * Thrown by {@link Configuration#setSetting(String, String)}; The setting name was recognized, but its value
+ * Thrown by {@link ExtendableBuilder#setSetting(String, String)}; The setting name was recognized, but its value
  * couldn't be parsed or the setting couldn't be set for some other reason. This exception should always have a
  * cause exception.
  */
 @SuppressWarnings("serial")
 public class ConfigurationSettingValueException extends ConfigurationException {
 
-    ConfigurationSettingValueException(String name, String value, Throwable cause) {
-        super(createMessage(name, value, "; see cause exception.", ""), cause);
-        _NullArgumentException.check("cause", cause);
+    public ConfigurationSettingValueException(String name, String value, Throwable cause) {
+        this(name, value, true, null, cause);
+    }
+
+    public ConfigurationSettingValueException(String name, String value, String reason) {
+        this(name, value, true, reason, null);
     }
 
-    ConfigurationSettingValueException(String name, String value, String reason) {
-        super(createMessage(name, value, ", because: ", reason));
-        _NullArgumentException.check("reason", reason);
+    /**
+     * @param name
+     *         The name of the setting
+     * @param value
+     *         The value of the setting
+     * @param showValue
+     *         Whether the value of the setting should be shown in the error message. Set to {@code false} if you want
+     *         to avoid {@link #toString()}-ing the {@code value}.
+     * @param reason
+     *         The explanation of why setting the setting has failed; maybe {@code null}, especially if you have a cause
+     *         exception anyway.
+     * @param cause
+     *         The cause exception of this exception (why setting the setting was failed)
+     */
+    public ConfigurationSettingValueException(String name, Object value, boolean showValue, String reason,
+            Throwable cause) {
+        super(
+                createMessage(
+                    name, value, true,
+                    reason != null ? ", because: " : (cause != null ? "; see cause exception." : null),
+                    reason),
+                cause);
     }
 
-    private static String createMessage(String name, String value, String detail1, String detail2) {
-        return "Failed to set FreeMarker configuration setting " + _StringUtil.jQuote(name)
-                + " to value " + _StringUtil.jQuote(value) + detail1 + detail2;
+    private static String createMessage(String name, Object value, boolean showValue, String detail1, String detail2) {
+        StringBuilder sb = new StringBuilder(64);
+        sb.append("Failed to set FreeMarker configuration setting ").append(_StringUtil.jQuote(name));
+        if (showValue) {
+            sb.append(" to value ")
+                    .append(
+                            value instanceof Number || value instanceof Boolean || value instanceof Date ? value
+                            : _StringUtil.jQuote(value));
+        } else {
+            sb.append(" to the specified value");
+        }
+        if (detail1 != null) {
+            sb.append(detail1);
+        }
+        if (detail2 != null) {
+            sb.append(detail2);
+        }
+        return sb.toString();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Environment.java b/src/main/java/org/apache/freemarker/core/Environment.java
index 220f305..6713200 100644
--- a/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/src/main/java/org/apache/freemarker/core/Environment.java
@@ -922,17 +922,17 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected TemplateExceptionHandler getInheritedTemplateExceptionHandler() {
+    protected TemplateExceptionHandler getDefaultTemplateExceptionHandler() {
         return getMainTemplate().getTemplateExceptionHandler();
     }
 
     @Override
-    protected ArithmeticEngine getInheritedArithmeticEngine() {
+    protected ArithmeticEngine getDefaultArithmeticEngine() {
         return getMainTemplate().getArithmeticEngine();
     }
 
     @Override
-    protected ObjectWrapper getInheritedObjectWrapper() {
+    protected ObjectWrapper getDefaultObjectWrapper() {
         return getMainTemplate().getObjectWrapper();
     }
 
@@ -962,7 +962,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected Locale getInheritedLocale() {
+    protected Locale getDefaultLocale() {
         return getMainTemplate().getLocale();
     }
 
@@ -991,7 +991,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected TimeZone getInheritedTimeZone() {
+    protected TimeZone getDefaultTimeZone() {
         return getMainTemplate().getTimeZone();
     }
 
@@ -1020,7 +1020,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected TimeZone getInheritedSQLDateAndTimeTimeZone() {
+    protected TimeZone getDefaultSQLDateAndTimeTimeZone() {
         return getMainTemplate().getSQLDateAndTimeTimeZone();
     }
 
@@ -1051,60 +1051,65 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected Charset getInheritedURLEscapingCharset() {
+    protected Charset getDefaultURLEscapingCharset() {
         return getMainTemplate().getURLEscapingCharset();
     }
 
     @Override
-    protected TemplateClassResolver getInheritedNewBuiltinClassResolver() {
+    protected TemplateClassResolver getDefaultNewBuiltinClassResolver() {
         return getMainTemplate().getNewBuiltinClassResolver();
     }
 
     @Override
-    protected boolean getInheritedAutoFlush() {
+    protected boolean getDefaultAutoFlush() {
         return getMainTemplate().getAutoFlush();
     }
 
     @Override
-    protected boolean getInheritedShowErrorTips() {
+    protected boolean getDefaultShowErrorTips() {
         return getMainTemplate().getShowErrorTips();
     }
 
     @Override
-    protected boolean getInheritedAPIBuiltinEnabled() {
+    protected boolean getDefaultAPIBuiltinEnabled() {
         return getMainTemplate().getAPIBuiltinEnabled();
     }
 
     @Override
-    protected boolean getInheritedLogTemplateExceptions() {
+    protected boolean getDefaultLogTemplateExceptions() {
         return getMainTemplate().getLogTemplateExceptions();
     }
 
     @Override
-    protected boolean getInheritedLazyImports() {
+    protected boolean getDefaultLazyImports() {
         return getMainTemplate().getLazyImports();
     }
 
     @Override
-    protected Boolean getInheritedLazyAutoImports() {
+    protected Boolean getDefaultLazyAutoImports() {
         return getMainTemplate().getLazyAutoImports();
     }
 
     @Override
-    protected Map<String, String> getInheritedAutoImports() {
+    protected Map<String, String> getDefaultAutoImports() {
         return getMainTemplate().getAutoImports();
     }
 
     @Override
-    protected List<String> getInheritedAutoIncludes() {
+    protected List<String> getDefaultAutoIncludes() {
         return getMainTemplate().getAutoIncludes();
     }
 
     @Override
-    protected Object getInheritedCustomAttribute(Object name) {
+    protected Object getDefaultCustomAttribute(Object name) {
         return getMainTemplate().getCustomAttribute(name);
     }
 
+    @Override
+    protected Map<Object, Object> getDefaultCustomAttributes() {
+        return getMainTemplate().getCustomAttributes();
+    }
+
     /*
      * Note that altough it's not allowed to set this setting with the <tt>setting</tt> directive, it still must be
      * allowed to set it from Java code while the template executes, since some frameworks allow templates to actually
@@ -1117,7 +1122,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected Charset getInheritedOutputEncoding() {
+    protected Charset getDefaultOutputEncoding() {
         return getMainTemplate().getOutputEncoding();
     }
 
@@ -1220,22 +1225,22 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected String getInheritedNumberFormat() {
+    protected String getDefaultNumberFormat() {
         return getMainTemplate().getNumberFormat();
     }
 
     @Override
-    protected Map<String, TemplateNumberFormatFactory> getInheritedCustomNumberFormats() {
+    protected Map<String, TemplateNumberFormatFactory> getDefaultCustomNumberFormats() {
         return getMainTemplate().getCustomNumberFormats();
     }
 
     @Override
-    protected TemplateNumberFormatFactory getInheritedCustomNumberFormat(String name) {
+    protected TemplateNumberFormatFactory getDefaultCustomNumberFormat(String name) {
         return getMainTemplate().getCustomNumberFormat(name);
     }
 
     @Override
-    protected String getInheritedBooleanFormat() {
+    protected String getDefaultBooleanFormat() {
         return getMainTemplate().getBooleanFormat();
     }
 
@@ -1530,7 +1535,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected String getInheritedTimeFormat() {
+    protected String getDefaultTimeFormat() {
         return getMainTemplate().getTimeFormat();
     }
 
@@ -1548,7 +1553,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected String getInheritedDateFormat() {
+    protected String getDefaultDateFormat() {
         return getMainTemplate().getDateFormat();
     }
 
@@ -1566,17 +1571,17 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
     }
 
     @Override
-    protected String getInheritedDateTimeFormat() {
+    protected String getDefaultDateTimeFormat() {
         return getMainTemplate().getDateTimeFormat();
     }
 
     @Override
-    protected Map<String, TemplateDateFormatFactory> getInheritedCustomDateFormats() {
+    protected Map<String, TemplateDateFormatFactory> getDefaultCustomDateFormats() {
         return getMainTemplate().getCustomDateFormats();
     }
 
     @Override
-    protected TemplateDateFormatFactory getInheritedCustomDateFormat(String name) {
+    protected TemplateDateFormatFactory getDefaultCustomDateFormat(String name) {
         return getMainTemplate().getCustomDateFormat(name);
     }
 
@@ -2083,7 +2088,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
         if (tm instanceof TemplateTransformModel) {
             ttm = (TemplateTransformModel) tm;
         } else if (exp instanceof ASTExpVariable) {
-            tm = configuration.getSharedVariable(exp.toString());
+            tm = configuration.getWrappedSharedVariable(exp.toString());
             if (tm instanceof TemplateTransformModel) {
                 ttm = (TemplateTransformModel) tm;
             }
@@ -2148,7 +2153,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
             result = rootDataModel.get(name);
         }
         if (result == null) {
-            result = configuration.getSharedVariable(name);
+            result = configuration.getWrappedSharedVariable(name);
         }
         return result;
     }
@@ -2200,7 +2205,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
      */
     public Set getKnownVariableNames() throws TemplateModelException {
         // shared vars.
-        Set set = configuration.getSharedVariableNames();
+        Set set = configuration.getSharedVariables().keySet();
 
         // root hash
         if (rootDataModel instanceof TemplateHashModelEx) {
@@ -2454,7 +2459,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
             public TemplateModel get(String key) throws TemplateModelException {
                 TemplateModel value = rootDataModel.get(key);
                 if (value == null) {
-                    value = configuration.getSharedVariable(key);
+                    value = configuration.getWrappedSharedVariable(key);
                 }
                 return value;
             }
@@ -2515,7 +2520,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen
                     result = rootDataModel.get(key);
                 }
                 if (result == null) {
-                    result = configuration.getSharedVariable(key);
+                    result = configuration.getWrappedSharedVariable(key);
                 }
                 return result;
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java b/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
index 2df4796..00a387d 100644
--- a/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
@@ -23,10 +23,10 @@ import java.io.InputStream;
 import java.nio.charset.Charset;
 
 import org.apache.freemarker.core.outputformat.OutputFormat;
+import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat;
 import org.apache.freemarker.core.templateresolver.TemplateLoader;
 import org.apache.freemarker.core.util._NullArgumentException;
 
-// TODO This will be the superclass of TemplateConfiguration.Builder and Configuration.Builder
 public abstract class MutableParsingAndProcessingConfiguration<
         SelfT extends MutableParsingAndProcessingConfiguration<SelfT>>
         extends MutableProcessingConfiguration<SelfT>
@@ -42,22 +42,30 @@ public abstract class MutableParsingAndProcessingConfiguration<
     private Charset sourceEncoding;
     private Integer tabSize;
 
-    protected MutableParsingAndProcessingConfiguration(Version incompatibleImprovements) {
-        super(incompatibleImprovements);
-    }
-
     protected MutableParsingAndProcessingConfiguration() {
         super();
     }
 
     /**
-     * See {@link Configuration#setTagSyntax(int)}.
+     * Setter pair of {@link #getTagSyntax()}.
      */
     public void setTagSyntax(int tagSyntax) {
-        Configuration.valideTagSyntaxValue(tagSyntax);
+        valideTagSyntaxValue(tagSyntax);
         this.tagSyntax = tagSyntax;
     }
 
+    // [FM3] Use enum; won't be needed
+    static void valideTagSyntaxValue(int tagSyntax) {
+        if (tagSyntax != ParsingConfiguration.AUTO_DETECT_TAG_SYNTAX
+                && tagSyntax != ParsingConfiguration.SQUARE_BRACKET_TAG_SYNTAX
+                && tagSyntax != ParsingConfiguration.ANGLE_BRACKET_TAG_SYNTAX) {
+            throw new IllegalArgumentException(
+                    "\"tagSyntax\" can only be set to one of these: "
+                    + "Configuration.AUTO_DETECT_TAG_SYNTAX, Configuration.ANGLE_BRACKET_SYNTAX, "
+                    + "or Configuration.SQUARE_BRACKET_SYNTAX");
+        }
+    }
+
     /**
      * Fluent API equivalent of {@link #tagSyntax(int)}
      */
@@ -67,32 +75,42 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
-     * The getter pair of {@link #setTagSyntax(int)}.
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ParsingConfiguration}).
      */
+    public void unsetTagSyntax() {
+        this.tagSyntax = null;
+    }
+
     @Override
     public int getTagSyntax() {
-        return tagSyntax != null ? tagSyntax : getInheritedTagSyntax();
+        return isTagSyntaxSet() ? tagSyntax : getDefaultTagSyntax();
     }
 
-    protected abstract int getInheritedTagSyntax();
+    /**
+     * Returns the value the getter method returns when the setting is not set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract int getDefaultTagSyntax();
 
     @Override
     public boolean isTagSyntaxSet() {
         return tagSyntax != null;
     }
 
-    /**
-     * See {@link Configuration#getTemplateLanguage()}
-     */
     @Override
     public TemplateLanguage getTemplateLanguage() {
-         return isTemplateLanguageSet() ? templateLanguage : getInheritedTemplateLanguage();
+         return isTemplateLanguageSet() ? templateLanguage : getDefaultTemplateLanguage();
     }
 
-    protected abstract TemplateLanguage getInheritedTemplateLanguage();
+    /**
+     * Returns the value the getter method returns when the setting is not set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract TemplateLanguage getDefaultTemplateLanguage();
 
     /**
-     * See {@link Configuration#setTemplateLanguage(TemplateLanguage)}
+     * Setter pair of {@link #getTemplateLanguage()}.
      */
     public void setTemplateLanguage(TemplateLanguage templateLanguage) {
         _NullArgumentException.check("templateLanguage", templateLanguage);
@@ -107,12 +125,21 @@ public abstract class MutableParsingAndProcessingConfiguration<
         return self();
     }
 
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ParsingConfiguration}).
+     */
+    public void unsetTemplateLanguage() {
+        this.templateLanguage = null;
+    }
+
+    @Override
     public boolean isTemplateLanguageSet() {
         return templateLanguage != null;
     }
 
     /**
-     * See {@link Configuration#setNamingConvention(int)}.
+     * Setter pair of {@link #getNamingConvention()}.
      */
     public void setNamingConvention(int namingConvention) {
         Configuration.validateNamingConventionValue(namingConvention);
@@ -128,15 +155,27 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ParsingConfiguration}).
+     */
+    public void unsetNamingConvention() {
+        this.namingConvention = null;
+    }
+
+    /**
      * The getter pair of {@link #setNamingConvention(int)}.
      */
     @Override
     public int getNamingConvention() {
          return isNamingConventionSet() ? namingConvention
-                : getInheritedNamingConvention();
+                : getDefaultNamingConvention();
     }
 
-    protected abstract int getInheritedNamingConvention();
+    /**
+     * Returns the value the getter method returns when the setting is not set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract int getDefaultNamingConvention();
 
     /**
      * Tells if this setting is set directly in this object or its value is inherited from the parent parsing configuration..
@@ -147,10 +186,10 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
-     * See {@link Configuration#setWhitespaceStripping(boolean)}.
+     * Setter pair of {@link ParsingConfiguration#getWhitespaceStripping()}.
      */
     public void setWhitespaceStripping(boolean whitespaceStripping) {
-        this.whitespaceStripping = Boolean.valueOf(whitespaceStripping);
+        this.whitespaceStripping = whitespaceStripping;
     }
 
     /**
@@ -162,15 +201,26 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ParsingConfiguration}).
+     */
+    public void unsetWhitespaceStripping() {
+        this.whitespaceStripping = null;
+    }
+
+    /**
      * The getter pair of {@link #getWhitespaceStripping()}.
      */
     @Override
     public boolean getWhitespaceStripping() {
-         return isWhitespaceStrippingSet() ? whitespaceStripping.booleanValue()
-                : getInheritedWhitespaceStripping();
+         return isWhitespaceStrippingSet() ? whitespaceStripping : getDefaultWhitespaceStripping();
     }
 
-    protected abstract boolean getInheritedWhitespaceStripping();
+    /**
+     * Returns the value the getter method returns when the setting is not set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract boolean getDefaultWhitespaceStripping();
 
     /**
      * Tells if this setting is set directly in this object or its value is inherited from the parent parsing configuration..
@@ -181,12 +231,24 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
-     * Sets the output format of the template; see {@link Configuration#setAutoEscapingPolicy(int)} for more.
+     * * Setter pair of {@link #getAutoEscapingPolicy()}.
      */
     public void setAutoEscapingPolicy(int autoEscapingPolicy) {
-        Configuration.validateAutoEscapingPolicyValue(autoEscapingPolicy);
+        validateAutoEscapingPolicyValue(autoEscapingPolicy);
+        this.autoEscapingPolicy = autoEscapingPolicy;
+    }
 
-        this.autoEscapingPolicy = Integer.valueOf(autoEscapingPolicy);
+    // [FM3] Use enum; won't be needed
+    static void validateAutoEscapingPolicyValue(int autoEscapingPolicy) {
+        if (autoEscapingPolicy != ParsingConfiguration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY
+                && autoEscapingPolicy != ParsingConfiguration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY
+                && autoEscapingPolicy != ParsingConfiguration.DISABLE_AUTO_ESCAPING_POLICY) {
+            throw new IllegalArgumentException(
+                    "\"tagSyntax\" can only be set to one of these: "
+                            + "Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY,"
+                            + "Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY, "
+                            + "or Configuration.DISABLE_AUTO_ESCAPING_POLICY");
+        }
     }
 
     /**
@@ -198,15 +260,26 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ParsingConfiguration}).
+     */
+    public void unsetAutoEscapingPolicy() {
+        this.autoEscapingPolicy = null;
+    }
+
+    /**
      * The getter pair of {@link #setAutoEscapingPolicy(int)}.
      */
     @Override
     public int getAutoEscapingPolicy() {
-         return isAutoEscapingPolicySet() ? autoEscapingPolicy.intValue()
-                : getInheritedAutoEscapingPolicy();
+         return isAutoEscapingPolicySet() ? autoEscapingPolicy : getDefaultAutoEscapingPolicy();
     }
 
-    protected abstract int getInheritedAutoEscapingPolicy();
+    /**
+     * Returns the value the getter method returns when the setting is not set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract int getDefaultAutoEscapingPolicy();
 
     /**
      * Tells if this setting is set directly in this object or its value is inherited from the parent parsing configuration..
@@ -217,14 +290,25 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
-     * Sets the output format of the template; see {@link Configuration#setOutputFormat(OutputFormat)} for more.
+     * Setter pair of {@link #getOutputFormat()}.
      */
     public void setOutputFormat(OutputFormat outputFormat) {
-        _NullArgumentException.check("outputFormat", outputFormat);
+        if (outputFormat == null) {
+            throw new _NullArgumentException(
+                    "outputFormat",
+                    "You may meant: " + UndefinedOutputFormat.class.getSimpleName() + ".INSTANCE");
+        }
         this.outputFormat = outputFormat;
     }
 
     /**
+     * Resets this setting to its initial state, as if it was never set.
+     */
+    public void unsetOutputFormat() {
+        this.outputFormat = null;
+    }
+
+    /**
      * Fluent API equivalent of {@link #setOutputFormat(OutputFormat)}
      */
     public SelfT outputFormat(OutputFormat outputFormat) {
@@ -232,15 +316,16 @@ public abstract class MutableParsingAndProcessingConfiguration<
         return self();
     }
 
-    /**
-     * The getter pair of {@link #setOutputFormat(OutputFormat)}.
-     */
     @Override
     public OutputFormat getOutputFormat() {
-         return isOutputFormatSet() ? outputFormat : getInheritedOutputFormat();
+         return isOutputFormatSet() ? outputFormat : getDefaultOutputFormat();
     }
 
-    protected abstract OutputFormat getInheritedOutputFormat();
+    /**
+     * Returns the value the getter method returns when the setting is not set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract OutputFormat getDefaultOutputFormat();
 
     /**
      * Tells if this setting is set directly in this object or its value is inherited from the parent parsing configuration..
@@ -251,10 +336,10 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
-     * See {@link Configuration#setRecognizeStandardFileExtensions(boolean)}.
+     * Setter pair of {@link ParsingConfiguration#getRecognizeStandardFileExtensions()}.
      */
     public void setRecognizeStandardFileExtensions(boolean recognizeStandardFileExtensions) {
-        this.recognizeStandardFileExtensions = Boolean.valueOf(recognizeStandardFileExtensions);
+        this.recognizeStandardFileExtensions = recognizeStandardFileExtensions;
     }
 
     /**
@@ -266,15 +351,26 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
+     * Resets this setting to its initial state, as if it was never set.
+     */
+    public void unsetRecognizeStandardFileExtensions() {
+        recognizeStandardFileExtensions = null;
+    }
+
+    /**
      * Getter pair of {@link #setRecognizeStandardFileExtensions(boolean)}.
      */
     @Override
     public boolean getRecognizeStandardFileExtensions() {
-         return isRecognizeStandardFileExtensionsSet() ? recognizeStandardFileExtensions.booleanValue()
-                : getInheritedRecognizeStandardFileExtensions();
+         return isRecognizeStandardFileExtensionsSet() ? recognizeStandardFileExtensions
+                : getDefaultRecognizeStandardFileExtensions();
     }
 
-    protected abstract boolean getInheritedRecognizeStandardFileExtensions();
+    /**
+     * Returns the value the getter method returns when the setting is not set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract boolean getDefaultRecognizeStandardFileExtensions();
 
     /**
      * Tells if this setting is set directly in this object or its value is inherited from the parent parsing configuration..
@@ -286,10 +382,14 @@ public abstract class MutableParsingAndProcessingConfiguration<
 
     @Override
     public Charset getSourceEncoding() {
-         return isSourceEncodingSet() ? sourceEncoding : getInheritedSourceEncoding();
+         return isSourceEncodingSet() ? sourceEncoding : getDefaultSourceEncoding();
     }
 
-    protected abstract Charset getInheritedSourceEncoding();
+    /**
+     * Returns the value the getter method returns when the setting is not set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract Charset getDefaultSourceEncoding();
 
     /**
      * The charset to be used when reading the template "file" that the {@link TemplateLoader} returns as binary
@@ -308,16 +408,30 @@ public abstract class MutableParsingAndProcessingConfiguration<
         return self();
     }
 
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ParsingConfiguration}).
+     */
+    public void unsetSourceEncoding() {
+        this.sourceEncoding = null;
+    }
+
+    @Override
     public boolean isSourceEncodingSet() {
         return sourceEncoding != null;
     }
 
     /**
-     * See {@link Configuration#setTabSize(int)}.
-     *
-     * @since 2.3.25
+     * Setter pair of {@link #getTabSize()}.
      */
     public void setTabSize(int tabSize) {
+        if (tabSize < 1) {
+            throw new IllegalArgumentException("\"tabSize\" must be at least 1, but was " + tabSize);
+        }
+        // To avoid integer overflows:
+        if (tabSize > 256) {
+            throw new IllegalArgumentException("\"tabSize\" can't be more than 256, but was " + tabSize);
+        }
         this.tabSize = Integer.valueOf(tabSize);
     }
 
@@ -330,16 +444,23 @@ public abstract class MutableParsingAndProcessingConfiguration<
     }
 
     /**
-     * Getter pair of {@link #setTabSize(int)}.
-     *
-     * @since 2.3.25
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ParsingConfiguration}).
      */
+    public void unsetTabSize() {
+        this.tabSize = null;
+    }
+
     @Override
     public int getTabSize() {
-         return isTabSizeSet() ? tabSize.intValue() : getInheritedTabSize();
+         return isTabSizeSet() ? tabSize.intValue() : getDefaultTabSize();
     }
 
-    protected abstract int getInheritedTabSize();
+    /**
+     * Returns the value the getter method returns when the setting is not set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link SettingValueNotSetException}.
+     */
+    protected abstract int getDefaultTabSize();
 
     /**
      * Tells if this setting is set directly in this object or its value is inherited from the parent parsing configuration..

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d61a45d/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java b/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
index a7daf31..c5c4c82 100644
--- a/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
+++ b/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java
@@ -22,7 +22,6 @@ package org.apache.freemarker.core;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -56,7 +55,6 @@ 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.TemplateLoader;
 import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat;
 import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2;
 import org.apache.freemarker.core.util.FTLUtil;
@@ -73,7 +71,7 @@ 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 anytime in future FreeMarker versions, so don't try to implement this
+ * 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>>
@@ -337,48 +335,27 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     private Map<Object, Object> customAttributes;
 
     /**
-     * Called by the {@link Configuration} constructor, initializes the fields to their {@link Configuration}-level
-     * default without marking them as set.
+     * 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.
      */
-    // TODO Move to Configuration(Builder) constructor
-    MutableProcessingConfiguration(Version iciForDefaults) {
-        _CoreAPI.checkVersionNotNullAndSupported(iciForDefaults);
-        locale = Configuration.getDefaultLocale();
-        timeZone = Configuration.getDefaultTimeZone();
-        sqlDateAndTimeTimeZone = null;
-        sqlDateAndTimeTimeZoneSet = true;
-        numberFormat = "number";
-        timeFormat = "";
-        dateFormat = "";
-        dateTimeFormat = "";
-        templateExceptionHandler = Configuration.getDefaultTemplateExceptionHandler();
-        arithmeticEngine = BigDecimalArithmeticEngine.INSTANCE;
-        objectWrapper = Configuration.getDefaultObjectWrapper(iciForDefaults);
-        autoFlush = Boolean.TRUE;
-        newBuiltinClassResolver = TemplateClassResolver.UNRESTRICTED_RESOLVER;
-        showErrorTips = Boolean.TRUE;
-        apiBuiltinEnabled = Boolean.FALSE;
-        logTemplateExceptions = Boolean.FALSE;
-        // outputEncoding and urlEscapingCharset defaults to null,
-        // which means "not specified"
-        setBooleanFormat(TemplateBooleanFormat.C_TRUE_FALSE);
-        customDateFormats = Collections.emptyMap();
-        customNumberFormats = Collections.emptyMap();
-        outputEncodingSet = true;
-        urlEscapingCharsetSet = true;
+    protected MutableProcessingConfiguration() {
+        // Empty
+    }
 
-        lazyImports = false;
-        lazyAutoImportsSet = true;
-        initAutoImportsMap();
-        initAutoIncludesList();
+    @Override
+    public Locale getLocale() {
+         return isLocaleSet() ? locale : getDefaultLocale();
     }
 
     /**
-     * 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.
+     * 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 MutableProcessingConfiguration() {
-        // Empty
+    protected abstract Locale getDefaultLocale();
+
+    @Override
+    public boolean isLocaleSet() {
+        return locale != null;
     }
 
     /**
@@ -397,21 +374,12 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         return self();
     }
 
-    @Override
-    public Locale getLocale() {
-         return isLocaleSet() ? locale : getInheritedLocale();
-    }
-
-    protected abstract Locale getInheritedLocale();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
      */
-    @Override
-    public boolean isLocaleSet() {
-        return locale != null;
+    public void unsetLocale() {
+        locale = null;
     }
 
     /**
@@ -429,19 +397,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedTimeZone();
+         return isTimeZoneSet() ? timeZone : getDefaultTimeZone();
     }
 
-    protected abstract TimeZone getInheritedTimeZone();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -463,20 +437,28 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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
-                : getInheritedSQLDateAndTimeTimeZone();
+                : getDefaultSQLDateAndTimeTimeZone();
     }
 
-    protected abstract TimeZone getInheritedSQLDateAndTimeTimeZone();
-
     /**
-     * Tells if this setting is set directly in this object or its value is inherited from a parent processing configuration.
-     *  
-     * @since 2.3.24
+     * 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;
@@ -498,18 +480,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedNumberFormat();
+         return isNumberFormatSet() ? numberFormat : getDefaultNumberFormat();
     }
 
-    protected abstract String getInheritedNumberFormat();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -517,10 +506,10 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     
     @Override
     public Map<String, TemplateNumberFormatFactory> getCustomNumberFormats() {
-         return isCustomNumberFormatsSet() ? customNumberFormats : getInheritedCustomNumberFormats();
+         return isCustomNumberFormatsSet() ? customNumberFormats : getDefaultCustomNumberFormats();
     }
 
-    protected abstract Map<String, TemplateNumberFormatFactory> getInheritedCustomNumberFormats();
+    protected abstract Map<String, TemplateNumberFormatFactory> getDefaultCustomNumberFormats();
 
     /**
      * Setter pair of {@link #getCustomNumberFormats()}. Note that custom number formats are get through
@@ -545,6 +534,14 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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) {
@@ -568,13 +565,6 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         }
     }
 
-    /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
-     *  
-     * @since 2.3.24
-     */
     @Override
     public boolean isCustomNumberFormatsSet() {
         return customNumberFormats != null;
@@ -589,10 +579,10 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
                 return r;
             }
         }
-        return getInheritedCustomNumberFormat(name);
+        return getDefaultCustomNumberFormat(name);
     }
 
-    protected abstract TemplateNumberFormatFactory getInheritedCustomNumberFormat(String name);
+    protected abstract TemplateNumberFormatFactory getDefaultCustomNumberFormat(String name);
 
     /**
      * Setter pair of {@link #getBooleanFormat()}.
@@ -617,21 +607,26 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedBooleanFormat();
+         return isBooleanFormatSet() ? booleanFormat : getDefaultBooleanFormat();
     }
 
-    protected abstract String getInheritedBooleanFormat();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
-     *  
-     * @since 2.3.24
+     * 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;
@@ -653,18 +648,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedTimeFormat();
+         return isTimeFormatSet() ? timeFormat : getDefaultTimeFormat();
     }
 
-    protected abstract String getInheritedTimeFormat();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -686,18 +688,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedDateFormat();
+         return isDateFormatSet() ? dateFormat : getDefaultDateFormat();
     }
 
-    protected abstract String getInheritedDateFormat();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -719,18 +728,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedDateTimeFormat();
+         return isDateTimeFormatSet() ? dateTimeFormat : getDefaultDateTimeFormat();
     }
 
-    protected abstract String getInheritedDateTimeFormat();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -738,10 +754,10 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
 
     @Override
     public Map<String, TemplateDateFormatFactory> getCustomDateFormats() {
-         return isCustomDateFormatsSet() ? customDateFormats : getInheritedCustomDateFormats();
+         return isCustomDateFormatsSet() ? customDateFormats : getDefaultCustomDateFormats();
     }
 
-    protected abstract Map<String, TemplateDateFormatFactory> getInheritedCustomDateFormats();
+    protected abstract Map<String, TemplateDateFormatFactory> getDefaultCustomDateFormats();
 
     /**
      * Setter pair of {@link #getCustomDateFormat(String)}.
@@ -761,10 +777,13 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     }
 
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -779,10 +798,10 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
                 return r;
             }
         }
-        return getInheritedCustomDateFormat(name);
+        return getDefaultCustomDateFormat(name);
     }
 
-    protected abstract TemplateDateFormatFactory getInheritedCustomDateFormat(String name);
+    protected abstract TemplateDateFormatFactory getDefaultCustomDateFormat(String name);
 
     /**
      * Setter pair of {@link #getTemplateExceptionHandler()}
@@ -800,19 +819,26 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedTemplateExceptionHandler();
+                ? templateExceptionHandler : getDefaultTemplateExceptionHandler();
     }
 
-    protected abstract TemplateExceptionHandler getInheritedTemplateExceptionHandler();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -834,18 +860,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedArithmeticEngine();
+         return isArithmeticEngineSet() ? arithmeticEngine : getDefaultArithmeticEngine();
     }
 
-    protected abstract ArithmeticEngine getInheritedArithmeticEngine();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -860,6 +893,14 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     }
 
     /**
+     * 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) {
@@ -870,16 +911,15 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     @Override
     public ObjectWrapper getObjectWrapper() {
          return isObjectWrapperSet()
-                ? objectWrapper : getInheritedObjectWrapper();
+                ? objectWrapper : getDefaultObjectWrapper();
     }
 
-    protected abstract ObjectWrapper getInheritedObjectWrapper();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -901,20 +941,28 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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
-                : getInheritedOutputEncoding();
+                : getDefaultOutputEncoding();
     }
 
-    protected abstract Charset getInheritedOutputEncoding();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -936,18 +984,26 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedURLEscapingCharset();
+        return isURLEscapingCharsetSet() ? urlEscapingCharset : getDefaultURLEscapingCharset();
     }
 
-    protected abstract Charset getInheritedURLEscapingCharset();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -969,21 +1025,26 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedNewBuiltinClassResolver();
+                ? newBuiltinClassResolver : getDefaultNewBuiltinClassResolver();
     }
 
-    protected abstract TemplateClassResolver getInheritedNewBuiltinClassResolver();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
-     *  
-     * @since 2.3.24
+     * 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;
@@ -993,7 +1054,7 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      * Setter pair of {@link #getAutoFlush()}
      */
     public void setAutoFlush(boolean autoFlush) {
-        this.autoFlush = Boolean.valueOf(autoFlush);
+        this.autoFlush = autoFlush;
     }
 
     /**
@@ -1004,20 +1065,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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.booleanValue() : getInheritedAutoFlush();
+         return isAutoFlushSet() ? autoFlush : getDefaultAutoFlush();
     }
 
-    protected abstract boolean getInheritedAutoFlush();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
-     *  
-     * @since 2.3.24
+     * 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;
@@ -1027,7 +1093,7 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      * Setter pair of {@link #getShowErrorTips()}
      */
     public void setShowErrorTips(boolean showTips) {
-        showErrorTips = Boolean.valueOf(showTips);
+        showErrorTips = showTips;
     }
 
     /**
@@ -1038,18 +1104,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedShowErrorTips();
+         return isShowErrorTipsSet() ? showErrorTips : getDefaultShowErrorTips();
     }
 
-    protected abstract boolean getInheritedShowErrorTips();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -1070,25 +1143,46 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         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 : getInheritedAPIBuiltinEnabled();
+         return isAPIBuiltinEnabledSet() ? apiBuiltinEnabled : getDefaultAPIBuiltinEnabled();
     }
 
-    protected abstract boolean getInheritedAPIBuiltinEnabled();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
-     *  
-     * @since 2.3.24
+     * 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()}
      */
@@ -1096,29 +1190,32 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         logTemplateExceptions = value;
     }
 
-    @Override
-    public boolean getLogTemplateExceptions() {
-         return isLogTemplateExceptionsSet() ? logTemplateExceptions : getInheritedLogTemplateExceptions();
+    /**
+     * Fluent API equivalent of {@link #setLogTemplateExceptions(boolean)}
+     */
+    public SelfT logTemplateExceptions(boolean value) {
+        setLogTemplateExceptions(value);
+        return self();
     }
 
-    protected abstract boolean getInheritedLogTemplateExceptions();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * Resets the setting value as if it was never set (but it doesn't affect the value inherited from another
+     * {@link ProcessingConfiguration}).
      */
-    @Override
-    public boolean isLogTemplateExceptionsSet() {
-        return logTemplateExceptions != null;
+    public void unsetLogTemplateExceptions() {
+        logTemplateExceptions = null;
     }
-    
+
     @Override
     public boolean getLazyImports() {
-         return isLazyImportsSet() ? lazyImports : getInheritedLazyImports();
+         return isLazyImportsSet() ? lazyImports : getDefaultLazyImports();
     }
 
-    protected abstract boolean getInheritedLazyImports();
+    /**
+     * 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()}
@@ -1128,21 +1225,36 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     }
 
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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 : getInheritedLazyAutoImports();
+        return isLazyAutoImportsSet() ? lazyAutoImports : getDefaultLazyAutoImports();
     }
 
-    protected abstract Boolean getInheritedLazyAutoImports();
+    /**
+     * 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()}
@@ -1151,12 +1263,24 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         this.lazyAutoImports = lazyAutoImports;
         lazyAutoImportsSet = true;
     }
-    
+
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -1219,21 +1343,34 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
             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 : getInheritedAutoImports();
+         return isAutoImportsSet() ? autoImports : getDefaultAutoImports();
     }
 
-    protected abstract Map<String,String> getInheritedAutoImports();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
-     * 
-     * @since 2.3.25
+     * 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;
@@ -1274,18 +1411,25 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         }
     }
 
+    /**
+     * Fluent API equivalent of {@link #setAutoIncludes(List)}
+     */
+    public SelfT autoIncludes(List templateNames) {
+        setAutoIncludes(templateNames);
+        return self();
+    }
+
     @Override
     public List<String> getAutoIncludes() {
-         return isAutoIncludesSet() ? autoIncludes : getInheritedAutoIncludes();
+         return isAutoIncludesSet() ? autoIncludes : getDefaultAutoIncludes();
     }
 
-    protected abstract List<String> getInheritedAutoIncludes();
-
     /**
-     * Tells if this setting is set directly in this object. If not, then depending on the implementing class, reading
-     * the setting might returns a default value, or returns the value of the setting from a parent processing
-     * configuration or throws a {@link SettingValueNotSetException}.
+     * 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;
@@ -1402,22 +1546,22 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      *       <br>String value: {@code "true"}, {@code "false"}, {@code "y"},  etc.
      *       
      *   <li><p>{@code "auto_import"}:
-     *       See {@link Configuration#setAutoImports(Map)}
+     *       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#setAutoIncludes(List)}
+     *       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#setLazyAutoImports(Boolean)}.
+     *       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#setLazyImports(boolean)}.
+     *       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.
      *       
@@ -1490,43 +1634,43 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      * <p>{@link Configuration} (a subclass of {@link MutableProcessingConfiguration}) also understands these:</p>
      * <ul>
      *   <li><p>{@code "auto_escaping"}:
-     *       See {@link Configuration#setAutoEscapingPolicy(int)}
+     *       See {@link Configuration#getAutoEscapingPolicy()}
      *       <br>String value: {@code "enable_if_default"} or {@code "enableIfDefault"} for
-     *       {@link Configuration#ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY},
+     *       {@link ParsingConfiguration#ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY},
      *       {@code "enable_if_supported"} or {@code "enableIfSupported"} for
-     *       {@link Configuration#ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}
-     *       {@code "disable"} for {@link Configuration#DISABLE_AUTO_ESCAPING_POLICY}.
+     *       {@link ParsingConfiguration#ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}
+     *       {@code "disable"} for {@link ParsingConfiguration#DISABLE_AUTO_ESCAPING_POLICY}.
      *       
      *   <li><p>{@code "sourceEncoding"}:
-     *       See {@link Configuration#setSourceEncoding(Charset)}; since 2.3.26 also accepts value "JVM default"
+     *       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#setLocalizedLookup}.
+     *       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 Configuration#setOutputFormat(OutputFormat)}.
+     *       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#setRegisteredCustomOutputFormats(Collection)}.
+     *       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 Configuration#setWhitespaceStripping}.
+     *       See {@link ParsingConfiguration#getWhitespaceStripping()}.
      *       <br>String value: {@code "true"}, {@code "false"}, {@code yes}, etc.
      *       
      *   <li><p>{@code "cache_storage"}:
-     *       See {@link Configuration#setCacheStorage}.
+     *       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,
@@ -1549,48 +1693,48 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      *       
      *   <li><p>{@code "template_update_delay"}:
      *       Template update delay in <b>seconds</b> (not in milliseconds) if no unit is specified; see
-     *       {@link Configuration#setTemplateUpdateDelayMilliseconds(long)} for more.
+     *       {@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 Configuration#setTagSyntax(int)}.
+     *       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 Configuration#setNamingConvention(int)}.
+     *       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#setIncompatibleImprovements(Version)}.
+     *       See {@link Configuration#getIncompatibleImprovements()}.
      *       <br>String value: version number like {@code 2.3.20}.
      *       
      *   <li><p>{@code "recognize_standard_file_extensions"}:
-     *       See {@link Configuration#setRecognizeStandardFileExtensions(boolean)}.
+     *       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#setTemplateConfigurations(org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory)}.
+     *       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#setTemplateLoader(TemplateLoader)}.
+     *       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#setTemplateLookupStrategy(org.apache.freemarker.core.templateresolver.TemplateLookupStrategy)}.
+     *       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#setTemplateNameFormat(org.apache.freemarker.core.templateresolver.TemplateNameFormat)}.
+     *       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}.
@@ -1743,8 +1887,9 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
                     } else if ("rethrow".equalsIgnoreCase(value)) {
                         setTemplateExceptionHandler(
                                 TemplateExceptionHandler.RETHROW_HANDLER);
-                    } else if (DEFAULT_VALUE.equalsIgnoreCase(value) && this instanceof Configuration) {
-                        ((Configuration) this).unsetTemplateExceptionHandler();
+                    } else if (DEFAULT_VALUE.equalsIgnoreCase(value)
+                            && this instanceof Configuration.ExtendableBuilder) {
+                        unsetTemplateExceptionHandler();
                     } else {
                         throw new ConfigurationSettingValueException(
                                 name, value,
@@ -1770,12 +1915,14 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
                 }
             } 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) {
-                        ((Configuration) this).unsetObjectWrapper();
+                    if (this instanceof Configuration.ExtendableBuilder) {
+                        this.unsetObjectWrapper();
                     } else {
-                        setObjectWrapper(Configuration.getDefaultObjectWrapper(Configuration.VERSION_3_0_0));
+                        // FM3 TODO should depend on IcI
+                        setObjectWrapper(new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build());
                     }
                 } else if ("restricted".equalsIgnoreCase(value)) {
+                    // FM3 TODO should depend on IcI
                     setObjectWrapper(new RestrictedObjectWrapper.Builder(Configuration.VERSION_3_0_0).build());
                 } else {
                     setObjectWrapper((ObjectWrapper) _ObjectBuilderSettingEvaluator.eval(
@@ -1857,6 +2004,14 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     }
 
     /**
+     * Fluent API equivalent of {@link #setSetting(String, String)}.
+     */
+    public SelfT setting(String name, String value) throws ConfigurationException {
+        setSetting(name, value);
+        return self();
+    }
+
+    /**
      * @throws IllegalArgumentException
      *             if the type of the some of the values isn't as expected
      */
@@ -1879,11 +2034,11 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
      *            If we want the setting names with camel case naming convention, or with snake case (legacy) naming
      *            convention.
      * 
-     * @see Configuration#getSettingNames(boolean)
+     * @see Configuration.ExtendableBuilder#getSettingNames(boolean)
      * 
      * @since 2.3.24
      */
-    public Set<String> getSettingNames(boolean camelCase) {
+    public static Set<String> getSettingNames(boolean camelCase) {
         return new _SortedArraySet<>(camelCase ? SETTING_NAMES_CAMEL_CASE : SETTING_NAMES_SNAKE_CASE);
     }
 
@@ -1945,11 +2100,21 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         }
     }
 
+    /**
+     * Fluent API equivalent of {@link #setSettings(Properties)}.
+     */
+    public SelfT settings(Properties props) {
+        setSettings(props);
+        return self();
+    }
+
     @Override
     public Map<Object, Object> getCustomAttributes() {
-        return customAttributes;
+        return isCustomAttributesSet() ? customAttributes : getDefaultCustomAttributes();
     }
 
+    protected abstract Map<Object,Object> getDefaultCustomAttributes();
+
     /**
      * Setter pair of {@link #getCustomAttributes()}
      *
@@ -1960,6 +2125,14 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     }
 
     /**
+     * Fluent API equivalent of {@link #setCustomAttributes(Map)}
+     */
+    public SelfT customAttributes(Map<Object, Object> customAttributes) {
+        setCustomAttributes(customAttributes);
+        return self();
+    }
+
+    /**
      * Used internally instead of {@link #setCustomAttributes(Map)} to speed up use cases where we know that there
      * won't be aliasing problems.
      */
@@ -1995,6 +2168,14 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
     }
 
     /**
+     * Fluent API equivalent of {@link #setCustomAttribute(Object, Object)}
+     */
+    public SelfT customAttribute(Object name, Object value) {
+        setCustomAttribute(name, value);
+        return self();
+    }
+
+    /**
      * Returns an array with names of all custom attributes defined directly on this {@link ProcessingConfiguration}.
      * (That is, it doesn't contain the names of custom attributes inherited from other {@link
      * ProcessingConfiguration}-s.) The returned array is never {@code null}, but can be zero-length.
@@ -2043,10 +2224,10 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
         } else {
             value = null;
         }
-        return value != null ? value : getInheritedCustomAttribute(key);
+        return value != null ? value : getDefaultCustomAttribute(key);
     }
 
-    protected abstract Object getInheritedCustomAttribute(Object name);
+    protected abstract Object getDefaultCustomAttribute(Object name);
 
     protected final List<String> parseAsList(String text) throws GenericParseException {
         return new SettingStringParser(text).parseAsList();