freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [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
Date Fri, 05 May 2017 21:00:32 GMT
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();



Mime
View raw message