Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 2f1f291dd -> 8915ac9bd


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
index 574fa82..356e6ed 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
@@ -19,6 +19,8 @@
 
 package org.apache.freemarker.core;
 
+import static org.apache.freemarker.core.Configuration.ExtendableBuilder.*;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Serializable;
@@ -76,7 +78,6 @@ import 
org.apache.freemarker.core.templateresolver.impl.MruCacheStorage;
 import org.apache.freemarker.core.templateresolver.impl.SoftCacheStorage;
 import org.apache.freemarker.core.util.BugException;
 import org.apache.freemarker.core.util.CaptureOutput;
-import org.apache.freemarker.core.util.CommonBuilder;
 import org.apache.freemarker.core.util.HtmlEscape;
 import org.apache.freemarker.core.util.NormalizeNewlines;
 import org.apache.freemarker.core.util.StandardCompress;
@@ -137,8 +138,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
  * <p>The setting reader methods of this class don't throw {@link 
CoreSettingValueNotSetException}, because all settings
  * are set on the {@link Configuration} level (even if they were just 
initialized to a default value).
  */
-public final class Configuration
-        implements TopLevelConfiguration, CustomStateScope {
+public final class Configuration implements TopLevelConfiguration, 
CustomStateScope {
     
     private static final String VERSION_PROPERTIES_PATH = 
"org/apache/freemarker/core/version.properties";
 
@@ -237,8 +237,14 @@ public final class Configuration
     // Configuration-specific settings:
 
     private final Version incompatibleImprovements;
-    private final DefaultTemplateResolver templateResolver;
-    private final boolean localizedLookup;
+    private final TemplateResolver templateResolver;
+    private final TemplateLoader templateLoader;
+    private final CacheStorage cacheStorage;
+    private final TemplateLookupStrategy templateLookupStrategy;
+    private final TemplateNameFormat templateNameFormat;
+    private final TemplateConfigurationFactory templateConfigurations;
+    private final Long templateUpdateDelayMilliseconds;
+    private final Boolean localizedLookup;
     private final List<OutputFormat> registeredCustomOutputFormats;
     private final Map<String, OutputFormat> 
registeredCustomOutputFormatsByName;
     private final Map<String, Object> sharedVariables;
@@ -291,20 +297,9 @@ public final class Configuration
 
     private <SelfT extends ExtendableBuilder<SelfT>> 
Configuration(ExtendableBuilder<SelfT> builder)
             throws ConfigurationException {
-        // Configuration-specific settings:
-
+        // Configuration-specific settings (except templateResolver):
         incompatibleImprovements = builder.getIncompatibleImprovements();
 
-        templateResolver = new DefaultTemplateResolver(
-                builder.getTemplateLoader(),
-                builder.getCacheStorage(), 
builder.getTemplateUpdateDelayMilliseconds(),
-                builder.getTemplateLookupStrategy(), 
builder.getLocalizedLookup(),
-                builder.getTemplateNameFormat(),
-                builder.getTemplateConfigurations(),
-                this);
-
-        localizedLookup = builder.getLocalizedLookup();
-
         {
             Collection<OutputFormat> registeredCustomOutputFormats = 
builder.getRegisteredCustomOutputFormats();
 
@@ -438,6 +433,67 @@ public final class Configuration
         lazyImports = builder.getLazyImports();
         lazyAutoImports = builder.getLazyAutoImports();
         customSettings = builder.getCustomSettings(false);
+
+        // Configuration-specific settings continued... templateResolver):
+
+        templateResolver = builder.getTemplateResolver();
+
+        templateLoader = builder.getTemplateLoader();
+        if (!templateResolver.supportsTemplateLoaderSetting()) {
+            checkSettingIsNullForThisTemplateResolver(
+                    templateResolver, TEMPLATE_LOADER_KEY, templateLoader);
+        }
+
+        cacheStorage = builder.getCacheStorage();
+        if (!templateResolver.supportsCacheStorageSetting()) {
+            checkSettingIsNullForThisTemplateResolver(
+                    templateResolver, CACHE_STORAGE_KEY, cacheStorage);
+        }
+
+        templateUpdateDelayMilliseconds = 
builder.getTemplateUpdateDelayMilliseconds();
+        if 
(!templateResolver.supportsTemplateUpdateDelayMillisecondsSetting()) {
+            checkSettingIsNullForThisTemplateResolver(
+                    templateResolver, TEMPLATE_UPDATE_DELAY_KEY, 
templateUpdateDelayMilliseconds);
+        }
+
+        templateLookupStrategy = builder.getTemplateLookupStrategy();
+        if (!templateResolver.supportsTemplateLookupStrategySetting()) {
+            checkSettingIsNullForThisTemplateResolver(
+                    templateResolver, TEMPLATE_LOOKUP_STRATEGY_KEY, 
templateLookupStrategy);
+        }
+
+        localizedLookup = builder.getLocalizedLookup();
+        if (!templateResolver.supportsLocalizedLookupSetting()) {
+            checkSettingIsNullForThisTemplateResolver(
+                    templateResolver, LOCALIZED_LOOKUP_KEY, localizedLookup);
+        }
+
+        templateNameFormat = builder.getTemplateNameFormat();
+        if (!templateResolver.supportsTemplateNameFormatSetting()) {
+            checkSettingIsNullForThisTemplateResolver(
+                    templateResolver, TEMPLATE_NAME_FORMAT_KEY, 
templateNameFormat);
+        }
+
+        templateConfigurations = builder.getTemplateConfigurations();
+        if (!templateResolver.supportsTemplateConfigurationsSetting()) {
+            checkSettingIsNullForThisTemplateResolver(
+                    templateResolver, TEMPLATE_CONFIGURATIONS_KEY, 
templateConfigurations);
+        }
+
+        templateResolver.setDependencies(new 
TemplateResolverDependenciesImpl(this, templateResolver));
+    }
+
+    private void checkSettingIsNullForThisTemplateResolver(
+            TemplateResolver templateResolver,
+            String settingName, Object value) {
+        if (value != null) {
+            throw new ConfigurationSettingValueException(
+                    settingName, null, false,
+                    "The templateResolver is a "
+                    + templateResolver.getClass().getName() + ", which doesn't 
support this setting, hence it "
+                    + "mustn't be set or must be set to null.",
+                    null);
+        }
     }
 
     private <SelfT extends ExtendableBuilder<SelfT>> void 
wrapAndPutSharedVariables(
@@ -478,11 +534,24 @@ public final class Configuration
     }
 
     @Override
+    public TemplateResolver getTemplateResolver() {
+        return templateResolver;
+    }
+
+
+
+    /**
+     * Always {@code true} in {@link Configuration}-s; even if this setting 
wasn't set in the builder, it gets a default
+     * value in the {@link Configuration}.
+     */
+    @Override
+    public boolean isTemplateResolverSet() {
+        return true;
+    }
+
+    @Override
     public TemplateLoader getTemplateLoader() {
-        if (templateResolver == null) {
-            return null;
-        }
-        return templateResolver.getTemplateLoader();
+        return templateLoader;
     }
 
     /**
@@ -496,10 +565,7 @@ public final class Configuration
 
     @Override
     public TemplateLookupStrategy getTemplateLookupStrategy() {
-        if (templateResolver == null) {
-            return null;
-        }
-        return templateResolver.getTemplateLookupStrategy();
+        return templateLookupStrategy;
     }
 
     /**
@@ -513,10 +579,7 @@ public final class Configuration
     
     @Override
     public TemplateNameFormat getTemplateNameFormat() {
-        if (templateResolver == null) {
-            return null;
-        }
-        return templateResolver.getTemplateNameFormat();
+        return templateNameFormat;
     }
 
     /**
@@ -530,10 +593,7 @@ public final class Configuration
 
     @Override
     public TemplateConfigurationFactory getTemplateConfigurations() {
-        if (templateResolver == null) {
-            return null;
-        }
-        return templateResolver.getTemplateConfigurations();
+        return templateConfigurations;
     }
 
     /**
@@ -547,7 +607,7 @@ public final class Configuration
 
     @Override
     public CacheStorage getCacheStorage() {
-        return templateResolver.getCacheStorage();
+        return cacheStorage;
     }
 
     /**
@@ -560,8 +620,8 @@ public final class Configuration
     }
 
     @Override
-    public long getTemplateUpdateDelayMilliseconds() {
-        return templateResolver.getTemplateUpdateDelayMilliseconds();
+    public Long getTemplateUpdateDelayMilliseconds() {
+        return templateUpdateDelayMilliseconds;
     }
 
     /**
@@ -1350,7 +1410,7 @@ public final class Configuration
         if (locale == null) {
             locale = getLocale();
         }
-        final GetTemplateResult maybeTemp = templateResolver.getTemplate(name, 
locale, customLookupCondition);
+        final GetTemplateResult maybeTemp = 
getTemplateResolver().getTemplate(name, locale, customLookupCondition);
         final Template temp = maybeTemp.getTemplate();
         if (temp == null) {
             if (ignoreMissing) {
@@ -1455,7 +1515,7 @@ public final class Configuration
      * <p>This method is thread-safe and can be called while the engine 
processes templates.
      */
     public void clearTemplateCache() {
-        templateResolver.clearTemplateCache();
+        getTemplateResolver().clearTemplateCache();
     }
     
     /**
@@ -1472,12 +1532,12 @@ public final class Configuration
      */
     public void removeTemplateFromCache(String name, Locale locale, 
Serializable customLookupCondition)
             throws IOException {
-        templateResolver.removeTemplateFromCache(name, locale, 
customLookupCondition);
+        getTemplateResolver().removeTemplateFromCache(name, locale, 
customLookupCondition);
     }
 
     @Override
-    public boolean getLocalizedLookup() {
-        return templateResolver.getLocalizedLookup();
+    public Boolean getLocalizedLookup() {
+        return localizedLookup;
     }
 
     /**
@@ -1600,7 +1660,7 @@ public final class Configuration
      */
     public abstract static class ExtendableBuilder<SelfT extends 
ExtendableBuilder<SelfT>>
             extends MutableParsingAndProcessingConfiguration<SelfT>
-            implements TopLevelConfiguration, CommonBuilder<Configuration> {
+            implements TopLevelConfiguration, 
org.apache.freemarker.core.util.CommonBuilder<Configuration> {
 
         /** Legacy, snake case ({@code like_this}) variation of the setting 
name. */
         public static final String SOURCE_ENCODING_KEY_SNAKE_CASE = 
"source_encoding";
@@ -1726,16 +1786,23 @@ public final class Configuration
         // Set early in the constructor to non-null
         private Version incompatibleImprovements = Configuration.VERSION_3_0_0;
 
+        private TemplateResolver templateResolver;
+        private TemplateResolver cachedDefaultTemplateResolver;
         private TemplateLoader templateLoader;
         private boolean templateLoaderSet;
         private CacheStorage cacheStorage;
+        private boolean cacheStorageSet;
         private CacheStorage cachedDefaultCacheStorage;
         private TemplateLookupStrategy templateLookupStrategy;
+        private boolean templateLookupStrategySet;
         private TemplateNameFormat templateNameFormat;
+        private boolean templateNameFormatSet;
         private TemplateConfigurationFactory templateConfigurations;
         private boolean templateConfigurationsSet;
         private Long templateUpdateDelayMilliseconds;
+        private boolean templateUpdateDelayMillisecondsSet;
         private Boolean localizedLookup;
+        private boolean localizedLookupSet;
 
         private Collection<OutputFormat> registeredCustomOutputFormats;
         private Map<String, Object> sharedVariables;
@@ -2024,8 +2091,48 @@ public final class Configuration
         }
 
         @Override
+        public TemplateResolver getTemplateResolver() {
+            return isTemplateResolverSet() ? templateResolver : 
getDefaultTemplateResolver();
+        }
+
+        @Override
+        public boolean isTemplateResolverSet() {
+            return templateResolver != null;
+        }
+
+        protected TemplateResolver getDefaultTemplateResolver() {
+            if (cachedDefaultTemplateResolver == null) {
+                cachedDefaultTemplateResolver = new DefaultTemplateResolver();
+            }
+            return cachedDefaultTemplateResolver;
+        }
+
+        /**
+         * Setter pair of {@link Configuration#getTemplateResolver()}; note 
{@code null}.
+         */
+        public void setTemplateResolver(TemplateResolver templateResolver) {
+            _NullArgumentException.check("templateResolver", templateResolver);
+            this.templateResolver = templateResolver;
+        }
+
+        /**
+         * Fluent API equivalent of {@link 
#setTemplateResolver(TemplateResolver)}
+         */
+        public SelfT templateResolver(TemplateResolver templateResolver) {
+            setTemplateResolver(templateResolver);
+            return self();
+        }
+
+        /**
+         * Resets this setting to its initial state, as if it was never set.
+         */
+        public void unsetTemplateResolver() {
+            templateResolver = null;
+        }
+
+        @Override
         public TemplateLoader getTemplateLoader() {
-            return isTemplateLoaderSet() ? templateLoader : 
getDefaultTemplateLoader();
+            return isTemplateLoaderSet() ? templateLoader : 
getDefaultTemplateLoaderTRAware();
         }
 
         @Override
@@ -2033,12 +2140,21 @@ public final class Configuration
             return templateLoaderSet;
         }
 
+        private TemplateLoader getDefaultTemplateLoaderTRAware() {
+            return isTemplateResolverSet() && 
!getTemplateResolver().supportsTemplateLoaderSetting() ?
+                    null : getDefaultTemplateLoader();
+        }
+
+        /**
+         * The default value when the {@link #getTemplateResolver() 
templateResolver} supports this setting (otherwise
+         * the default is hardwired to be {@code null} and this method isn't 
called).
+         */
         protected TemplateLoader getDefaultTemplateLoader() {
             return null;
         }
 
         /**
-         * Setter pair of {@link Configuration#getTemplateLoader()}.
+         * Setter pair of {@link Configuration#getTemplateLoader()}. Note that 
{@code null} is a valid value.
          */
         public void setTemplateLoader(TemplateLoader templateLoader) {
             this.templateLoader = templateLoader;
@@ -2063,14 +2179,23 @@ public final class Configuration
 
         @Override
         public CacheStorage getCacheStorage() {
-            return isCacheStorageSet() ? cacheStorage : 
getDefaultCacheStorage();
+            return isCacheStorageSet() ? cacheStorage : 
getDefaultCacheStorageTRAware();
         }
 
         @Override
         public boolean isCacheStorageSet() {
-            return cacheStorage != null;
+            return cacheStorageSet;
         }
 
+        private CacheStorage getDefaultCacheStorageTRAware() {
+            return isTemplateResolverSet() && 
!getTemplateResolver().supportsCacheStorageSetting() ? null
+                    : getDefaultCacheStorage();
+        }
+
+        /**
+         * The default value when the {@link #getTemplateResolver() 
templateResolver} supports this setting (otherwise
+         * the default is hardwired to be {@code null} and this method isn't 
called).
+         */
         protected CacheStorage getDefaultCacheStorage() {
             if (cachedDefaultCacheStorage == null) {
                 // If this will depend on incompatibleImprovements, null it 
out in onIncompatibleImprovementsChanged()!
@@ -2084,6 +2209,7 @@ public final class Configuration
          */
         public void setCacheStorage(CacheStorage cacheStorage) {
             this.cacheStorage = cacheStorage;
+            this.cacheStorageSet = true;
             cachedDefaultCacheStorage = null;
         }
 
@@ -2100,19 +2226,29 @@ public final class Configuration
          */
         public void unsetCacheStorage() {
             cacheStorage = null;
+            cacheStorageSet = false;
         }
 
         @Override
         public TemplateLookupStrategy getTemplateLookupStrategy() {
-            return isTemplateLookupStrategySet() ? templateLookupStrategy : 
getDefaultTemplateLookupStrategySet();
+            return isTemplateLookupStrategySet() ? templateLookupStrategy : 
getDefaultTemplateLookupStrategyTRAware();
         }
 
         @Override
         public boolean isTemplateLookupStrategySet() {
-            return templateLookupStrategy != null;
+            return templateLookupStrategySet;
+        }
+
+        private TemplateLookupStrategy 
getDefaultTemplateLookupStrategyTRAware() {
+            return isTemplateResolverSet() && 
!getTemplateResolver().supportsTemplateLookupStrategySetting() ? null :
+                    getDefaultTemplateLookupStrategy();
         }
 
-        protected TemplateLookupStrategy getDefaultTemplateLookupStrategySet() 
{
+        /**
+         * The default value when the {@link #getTemplateResolver() 
templateResolver} supports this setting (otherwise
+         * the default is hardwired to be {@code null} and this method isn't 
called).
+         */
+        protected TemplateLookupStrategy getDefaultTemplateLookupStrategy() {
             return DefaultTemplateLookupStrategy.INSTANCE;
         }
 
@@ -2120,8 +2256,8 @@ public final class Configuration
          * Setter pair of {@link Configuration#getTemplateLookupStrategy()}.
          */
         public void setTemplateLookupStrategy(TemplateLookupStrategy 
templateLookupStrategy) {
-            _NullArgumentException.check("templateLookupStrategy", 
templateLookupStrategy);
             this.templateLookupStrategy = templateLookupStrategy;
+            templateLookupStrategySet = true;
         }
 
         /**
@@ -2137,11 +2273,12 @@ public final class Configuration
          */
         public void unsetTemplateLookupStrategy() {
             templateLookupStrategy = null;
+            templateLookupStrategySet = false;
         }
 
         @Override
         public TemplateNameFormat getTemplateNameFormat() {
-            return isTemplateNameFormatSet() ? templateNameFormat : 
getDefaultTemplateNameFormat();
+            return isTemplateNameFormatSet() ? templateNameFormat : 
getDefaultTemplateNameFormatTRAware();
         }
 
         /**
@@ -2149,9 +2286,18 @@ public final class Configuration
          */
         @Override
         public boolean isTemplateNameFormatSet() {
-            return  templateNameFormat != null;
+            return templateNameFormatSet;
+        }
+
+        private TemplateNameFormat getDefaultTemplateNameFormatTRAware() {
+            return isTemplateResolverSet() && 
!getTemplateResolver().supportsTemplateNameFormatSetting() ? null
+                    : getDefaultTemplateNameFormat();
         }
 
+        /**
+         * The default value when the {@link #getTemplateResolver() 
templateResolver} supports this setting (otherwise
+         * the default is hardwired to be {@code null} and this method isn't 
called).
+         */
         protected TemplateNameFormat getDefaultTemplateNameFormat() {
             return DefaultTemplateNameFormatFM2.INSTANCE;
         }
@@ -2160,8 +2306,8 @@ public final class Configuration
          * Setter pair of {@link Configuration#getTemplateNameFormat()}.
          */
         public void setTemplateNameFormat(TemplateNameFormat 
templateNameFormat) {
-            _NullArgumentException.check("templateNameFormat", 
templateNameFormat);
             this.templateNameFormat = templateNameFormat;
+            templateNameFormatSet = true;
         }
 
         /**
@@ -2177,11 +2323,12 @@ public final class Configuration
          */
         public void unsetTemplateNameFormat() {
             this.templateNameFormat = null;
+            templateNameFormatSet = false;
         }
 
         @Override
         public TemplateConfigurationFactory getTemplateConfigurations() {
-            return isTemplateConfigurationsSet() ? templateConfigurations : 
getDefaultTemplateConfigurations();
+            return isTemplateConfigurationsSet() ? templateConfigurations : 
getDefaultTemplateConfigurationsTRAware();
         }
 
         @Override
@@ -2189,6 +2336,15 @@ public final class Configuration
             return templateConfigurationsSet;
         }
 
+        private TemplateConfigurationFactory 
getDefaultTemplateConfigurationsTRAware() {
+            return isTemplateResolverSet() && 
!getTemplateResolver().supportsTemplateConfigurationsSetting() ? null
+                    : getDefaultTemplateConfigurations();
+        }
+
+        /**
+         * The default value when the {@link #getTemplateResolver() 
templateResolver} supports this setting (otherwise
+         * the default is hardwired to be {@code null} and this method isn't 
called).
+         */
         protected TemplateConfigurationFactory 
getDefaultTemplateConfigurations() {
             return null;
         }
@@ -2218,31 +2374,41 @@ public final class Configuration
         }
 
         @Override
-        public long getTemplateUpdateDelayMilliseconds() {
+        public Long getTemplateUpdateDelayMilliseconds() {
             return isTemplateUpdateDelayMillisecondsSet() ? 
templateUpdateDelayMilliseconds
-                    : getDefaultTemplateUpdateDelayMilliseconds();
+                    : getDefaultTemplateUpdateDelayMillisecondsTRAware();
         }
 
         @Override
         public boolean isTemplateUpdateDelayMillisecondsSet() {
-            return templateUpdateDelayMilliseconds != null;
+            return templateUpdateDelayMillisecondsSet;
+        }
+
+        private Long getDefaultTemplateUpdateDelayMillisecondsTRAware() {
+            return isTemplateResolverSet() && 
!getTemplateResolver().supportsTemplateUpdateDelayMillisecondsSetting()
+                    ? null : getDefaultTemplateUpdateDelayMilliseconds();
         }
 
-        protected long getDefaultTemplateUpdateDelayMilliseconds() {
-            return 5000;
+        /**
+         * The default value when the {@link #getTemplateResolver() 
templateResolver} supports this setting (otherwise
+         * the default is hardwired to be {@code null} and this method isn't 
called).
+         */
+        protected Long getDefaultTemplateUpdateDelayMilliseconds() {
+            return 5000L;
         }
 
         /**
          * Setter pair of {@link 
Configuration#getTemplateUpdateDelayMilliseconds()}.
          */
-        public void setTemplateUpdateDelayMilliseconds(long 
templateUpdateDelayMilliseconds) {
+        public void setTemplateUpdateDelayMilliseconds(Long 
templateUpdateDelayMilliseconds) {
             this.templateUpdateDelayMilliseconds = 
templateUpdateDelayMilliseconds;
+            templateUpdateDelayMillisecondsSet = true;
         }
 
         /**
-         * Fluent API equivalent of {@link 
#setTemplateUpdateDelayMilliseconds(long)}
+         * Fluent API equivalent of {@link 
#setTemplateUpdateDelayMilliseconds(Long)}
          */
-        public SelfT templateUpdateDelayMilliseconds(long 
templateUpdateDelayMilliseconds) {
+        public SelfT templateUpdateDelayMilliseconds(Long 
templateUpdateDelayMilliseconds) {
             
setTemplateUpdateDelayMilliseconds(templateUpdateDelayMilliseconds);
             return self();
         }
@@ -2252,19 +2418,29 @@ public final class Configuration
          */
         public void unsetTemplateUpdateDelayMilliseconds() {
             templateUpdateDelayMilliseconds = null;
+            templateUpdateDelayMillisecondsSet = false;
         }
 
         @Override
-        public boolean getLocalizedLookup() {
-            return isLocalizedLookupSet() ? localizedLookup : 
getDefaultLocalizedLookup();
+        public Boolean getLocalizedLookup() {
+            return isLocalizedLookupSet() ? localizedLookup : 
getDefaultLocalizedLookupTRAware();
         }
 
         @Override
         public boolean isLocalizedLookupSet() {
-            return localizedLookup != null;
+            return localizedLookupSet;
+        }
+
+        private Boolean getDefaultLocalizedLookupTRAware() {
+            return isTemplateResolverSet() && 
!getTemplateResolver().supportsLocalizedLookupSetting() ? null
+                    : getDefaultLocalizedLookup();
         }
 
-        protected boolean getDefaultLocalizedLookup() {
+        /**
+         * The default value when the {@link #getTemplateResolver() 
templateResolver} supports this setting (otherwise
+         * the default is hardwired to be {@code null} and this method isn't 
called).
+         */
+        protected Boolean getDefaultLocalizedLookup() {
             return true;
         }
 
@@ -2273,6 +2449,7 @@ public final class Configuration
          */
         public void setLocalizedLookup(Boolean localizedLookup) {
             this.localizedLookup = localizedLookup;
+            localizedLookupSet = true;
         }
 
         /**
@@ -2288,6 +2465,7 @@ public final class Configuration
          */
         public void unsetLocalizedLookup() {
             this.localizedLookup = null;
+            localizedLookupSet = false;
         }
 
         public Collection<OutputFormat> getRegisteredCustomOutputFormats() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
index 3ed6512..193ec96 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ConfigurationSettingValueException.java
@@ -57,7 +57,7 @@ public class ConfigurationSettingValueException extends 
ConfigurationException {
             Throwable cause) {
         super(
                 createMessage(
-                    name, value, true,
+                    name, value, showValue,
                     reason != null ? ", because: " : (cause != null ? "; see 
cause exception." : null),
                     reason),
                 cause);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
index b3c4150..316bd00 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
@@ -61,9 +61,7 @@ import 
org.apache.freemarker.core.model.TemplateTransformModel;
 import org.apache.freemarker.core.model.TransformControl;
 import org.apache.freemarker.core.model.impl.SimpleHash;
 import 
org.apache.freemarker.core.templateresolver.MalformedTemplateNameException;
-import org.apache.freemarker.core.templateresolver.TemplateNameFormat;
 import org.apache.freemarker.core.templateresolver.TemplateResolver;
-import org.apache.freemarker.core.templateresolver._CacheAPI;
 import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat;
 import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2;
 import org.apache.freemarker.core.util.UndeclaredThrowableException;
@@ -2778,8 +2776,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
             // We can't cause a template lookup here (see 
TemplateLookupStrategy), as that can be expensive. We exploit
             // that (at least in 2.3.x) the name used for eager import 
namespace key isn't the template.sourceName, but
             // the looked up name (template.name), which we can get quickly:
-            TemplateNameFormat tnf = 
getConfiguration().getTemplateNameFormat();
-            templateName = _CacheAPI.normalizeRootBasedName(tnf, templateName);
+            templateName = 
getConfiguration().getTemplateResolver().normalizeRootBasedName(templateName);
         }
         
         if (loadedLibs == null) {
@@ -2857,19 +2854,7 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
         if (baseName == null) {
             return targetName;
         }
-        return 
_CacheAPI.toRootBasedName(configuration.getTemplateNameFormat(), baseName, 
targetName);
-    }
-
-    String renderElementToString(ASTElement te) throws IOException, 
TemplateException {
-        Writer prevOut = out;
-        try {
-            StringWriter sw = new StringWriter();
-            out = sw;
-            visit(te);
-            return sw.toString();
-        } finally {
-            out = prevOut;
-        }
+        return configuration.getTemplateResolver().toRootBasedName(baseName, 
targetName);
     }
 
     void importMacros(Template template) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
index 60a99ab..18e17f6 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
@@ -195,9 +195,8 @@ public interface ParsingConfiguration {
 
     /**
      * Tells if the "file" extension part of the source name ({@link 
Template#getSourceName()}) will influence certain
-     * parsing settings. For backward compatibility, it defaults to {@code 
false} if
-     * {@link #getIncompatibleImprovements()} is less than 2.3.24. Starting 
from {@code incompatibleImprovements}
-     * 2.3.24, it defaults to {@code true}, so the following standard file 
extensions take their effect:
+     * parsing settings. Defaults to {@code true}. When {@code true}, the 
following standard file extensions take
+     * their effect:
      *
      * <ul>
      *   <li>{@code ftlh}: Sets the {@link #getOutputFormat() outputFormat} 
setting to {@code "HTML"}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java
index 766669e..dc136e8 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/SettingValueNotSetException.java
@@ -25,10 +25,6 @@ package org.apache.freemarker.core;
  */
 public abstract class SettingValueNotSetException extends RuntimeException {
 
-    public SettingValueNotSetException(String message) {
-        super(message);
-    }
-
     public SettingValueNotSetException(String message, Throwable cause) {
         super(message, cause);
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateResolverDependenciesImpl.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateResolverDependenciesImpl.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateResolverDependenciesImpl.java
new file mode 100644
index 0000000..10b219f
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateResolverDependenciesImpl.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import static org.apache.freemarker.core.Configuration.ExtendableBuilder.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+import org.apache.freemarker.core.templateresolver.CacheStorage;
+import 
org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory;
+import org.apache.freemarker.core.templateresolver.TemplateLoader;
+import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy;
+import org.apache.freemarker.core.templateresolver.TemplateNameFormat;
+import org.apache.freemarker.core.templateresolver.TemplateResolver;
+import 
org.apache.freemarker.core.templateresolver.TemplateResolverDependencies;
+import org.apache.freemarker.core.util._NullArgumentException;
+
+/**
+ * Used internally by {@link Configuration}.
+ */
+class TemplateResolverDependenciesImpl extends TemplateResolverDependencies {
+
+    private final TemplateResolver templateResolver;
+    private final Configuration configuration;
+
+    public TemplateResolverDependenciesImpl(Configuration configuration, 
TemplateResolver templateResolver) {
+        _NullArgumentException.check("configuration", configuration);
+        _NullArgumentException.check("templateResolver", templateResolver);
+        this.templateResolver = templateResolver;
+        this.configuration = configuration;
+    }
+
+    @Override
+    public TemplateLoader getTemplateLoader() {
+        checkSettingSupported(TEMPLATE_LOADER_KEY, 
templateResolver.supportsTemplateLoaderSetting());
+        return configuration.getTemplateLoader();
+    }
+
+    @Override
+    public CacheStorage getCacheStorage() {
+        checkSettingSupported(CACHE_STORAGE_KEY, 
templateResolver.supportsCacheStorageSetting());
+        return configuration.getCacheStorage();
+    }
+
+    @Override
+    public TemplateLookupStrategy getTemplateLookupStrategy() {
+        checkSettingSupported(TEMPLATE_LOOKUP_STRATEGY_KEY, 
templateResolver.supportsTemplateLookupStrategySetting());
+        return configuration.getTemplateLookupStrategy();
+    }
+
+    @Override
+    public TemplateNameFormat getTemplateNameFormat() {
+        checkSettingSupported(TEMPLATE_NAME_FORMAT_KEY, 
templateResolver.supportsTemplateNameFormatSetting());
+        return configuration.getTemplateNameFormat();
+    }
+
+    @Override
+    public TemplateConfigurationFactory getTemplateConfigurations() {
+        checkSettingSupported(TEMPLATE_CONFIGURATIONS_KEY, 
templateResolver.supportsTemplateConfigurationsSetting());
+        return configuration.getTemplateConfigurations();
+    }
+
+    @Override
+    public Long getTemplateUpdateDelayMilliseconds() {
+        checkSettingSupported(
+                TEMPLATE_UPDATE_DELAY_KEY, 
templateResolver.supportsTemplateUpdateDelayMillisecondsSetting());
+        return configuration.getTemplateUpdateDelayMilliseconds();
+    }
+
+    @Override
+    public Boolean getLocalizedLookup() {
+        checkSettingSupported(LOCALIZED_LOOKUP_KEY, 
templateResolver.supportsLocalizedLookupSetting());
+        return configuration.getLocalizedLookup();
+    }
+
+    @Override
+    public Charset getSourceEncoding() {
+        return configuration.getSourceEncoding();
+    }
+
+    @Override
+    public TemplateLanguage getTemplateLanguage() {
+        return configuration.getTemplateLanguage();
+    }
+
+    private void checkSettingSupported(String name, boolean supported) {
+        if (!supported) {
+            throw new 
IllegalStateException(templateResolver.getClass().getName() + " reported that 
it doesn't support "
+                    + "this setting, so you aren't allowed to get it: " + 
name);
+        }
+    }
+
+    @Override
+    public Template parse(TemplateLanguage templateLanguage, String name, 
String sourceName, Reader reader,
+            TemplateConfiguration templateConfiguration, Charset encoding, 
InputStream streamToUnmarkWhenEncEstabd)
+            throws IOException, ParseException {
+        return templateLanguage.parse(name, sourceName, reader,
+                configuration, templateConfiguration, encoding, 
streamToUnmarkWhenEncEstabd);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java
index 5ad4490..18e705b 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java
@@ -27,10 +27,11 @@ import 
org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory;
 import org.apache.freemarker.core.templateresolver.TemplateLoader;
 import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy;
 import org.apache.freemarker.core.templateresolver.TemplateNameFormat;
+import org.apache.freemarker.core.templateresolver.TemplateResolver;
 import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateLookupStrategy;
 import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat;
 import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2;
-import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader;
+import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver;
 import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader;
 import org.apache.freemarker.core.templateresolver.impl.SoftCacheStorage;
 
@@ -43,16 +44,24 @@ import 
org.apache.freemarker.core.templateresolver.impl.SoftCacheStorage;
 public interface TopLevelConfiguration extends 
ParsingAndProcessingConfiguration {
 
     /**
-     * The {@link TemplateLoader} that is used to look up and load templates.
+     * The {@link TemplateResolver} to use; defaults to a {@link 
DefaultTemplateResolver}.
+     */
+    TemplateResolver getTemplateResolver();
+
+    /**
+     * Tells if this setting was explicitly set (otherwise its value will be 
the default value).
+     */
+    boolean isTemplateResolverSet();
+
+    /**
+     * The {@link TemplateLoader} that is used to look up and load templates; 
defaults to {@code null}.
      * By providing your own {@link TemplateLoader} implementation, you can 
load templates from whatever kind of
      * storages, like from relational databases, NoSQL-storages, etc.
      *
      * <p>You can chain several {@link TemplateLoader}-s together with {@link 
MultiTemplateLoader}.
      *
-     * <p>Default value: You should always set the template loader instead of 
relying on the default value.
-     * (But if you still care what it is, before "incompatible improvements" 
2.3.21 it's a {@link FileTemplateLoader}
-     * that uses the current directory as its root; as it's hard tell what 
that directory will be, it's not very useful
-     * and dangerous. Starting with "incompatible improvements" 2.3.21 the 
default is {@code null}.)
+     * <p>If the {@link #getTemplateResolver() templateResolver} doesn't 
support this setting, then it must be {@code
+     * null}. This check is postponed until the {@link Configuration} instance 
is created.
      */
     TemplateLoader getTemplateLoader();
 
@@ -63,7 +72,11 @@ public interface TopLevelConfiguration extends 
ParsingAndProcessingConfiguration
 
     /**
      * The {@link TemplateLookupStrategy} that is used to look up templates 
based on the requested name, locale and
-     * custom lookup condition. Its default is {@link 
DefaultTemplateLookupStrategy#INSTANCE}.
+     * custom lookup condition. Its default is {@link 
DefaultTemplateLookupStrategy#INSTANCE}, except when the
+     * {@link #getTemplateResolver() templateResolver} doesn't support this 
setting, in which case it's {@code null}.
+     *
+     * <p>If the {@link #getTemplateResolver() templateResolver} doesn't 
support this setting, then it must be {@code
+     * null}. This check is postponed until the {@link Configuration} instance 
is created.
      */
     TemplateLookupStrategy getTemplateLookupStrategy();
 
@@ -74,8 +87,12 @@ public interface TopLevelConfiguration extends 
ParsingAndProcessingConfiguration
 
     /**
      * The template name format used; see {@link TemplateNameFormat}. The 
default is
-     * {@link DefaultTemplateNameFormatFM2#INSTANCE}, while the recommended 
value for new projects is
-     * {@link DefaultTemplateNameFormat#INSTANCE}.
+     * {@link DefaultTemplateNameFormatFM2#INSTANCE} (while the recommended 
value for new projects is
+     * {@link DefaultTemplateNameFormat#INSTANCE}), except when the {@link 
#getTemplateResolver() templateResolver}
+     * doesn't support this setting, in which case it's {@code null}.
+     *
+     * <p>If the {@link #getTemplateResolver() templateResolver} doesn't 
support this setting, then it must be {@code
+     * null}. This check is postponed until the {@link Configuration} instance 
is created.
      */
     TemplateNameFormat getTemplateNameFormat();
 
@@ -86,14 +103,17 @@ public interface TopLevelConfiguration extends 
ParsingAndProcessingConfiguration
 
     /**
      * The {@link TemplateConfigurationFactory} that will configure individual 
templates where their settings differ
-     * from those coming from the common {@link Configuration} object. A 
typical use case for that is specifying the
-     * {@link #getOutputFormat() outputFormat} or {@link #getSourceEncoding() 
sourceEncoding} for templates based on
-     * their file extension or parent directory.
+     * from those coming from the common {@link Configuration} object. 
Defaults to {@code null}.
+     * A typical use case for that is specifying the {@link #getOutputFormat() 
outputFormat} or
+     * {@link #getSourceEncoding() sourceEncoding} for templates based on 
their file extension or parent directory.
      * <p>
      * Note that the settings suggested by standard file extensions are 
stronger than that you set here. See
      * {@link #getRecognizeStandardFileExtensions()} for more information 
about standard file extensions.
      * <p>
      * See "Template configurations" in the FreeMarker Manual for examples.
+     *
+     * <p>If the {@link #getTemplateResolver() templateResolver} doesn't 
support this setting, then it must be {@code
+     * null}. This check is postponed until the {@link Configuration} instance 
is created.
      */
     TemplateConfigurationFactory getTemplateConfigurations();
 
@@ -103,8 +123,12 @@ public interface TopLevelConfiguration extends 
ParsingAndProcessingConfiguration
     boolean isTemplateConfigurationsSet();
 
     /**
-     * The map-like object used for caching templates to avoid repeated 
loading and parsing of the template "files".
-     * Its {@link Configuration}-level default is a {@link SoftCacheStorage}.
+     * The map-like object used for caching templates to avoid repeated 
loading and parsing of the template "files" to
+     * {@link Template} objects. The default is a {@link SoftCacheStorage}, 
except when the
+     * {@link #getTemplateResolver() templateResolver} doesn't support this 
setting, in which case it's {@code null}.
+     *
+     * <p>If the {@link #getTemplateResolver() templateResolver} doesn't 
support this setting, then it must be {@code
+     * null}. This check is postponed until the {@link Configuration} instance 
is created.
      */
     CacheStorage getCacheStorage();
 
@@ -115,9 +139,13 @@ public interface TopLevelConfiguration extends 
ParsingAndProcessingConfiguration
 
     /**
      * The time in milliseconds that must elapse before checking whether there 
is a newer version of a template
-     * "file" than the cached one. Defaults to 5000 ms.
+     * "file" than the cached one. The defaults is 5000 ms, except when the
+     * {@link #getTemplateResolver() templateResolver} doesn't support this 
setting, in which case it's {@code null}.
+     *
+     * <p>If the {@link #getTemplateResolver() templateResolver} doesn't 
support this setting, then it must be {@code
+     * null}. This check is postponed until the {@link Configuration} instance 
is created.
      */
-    long getTemplateUpdateDelayMilliseconds();
+    Long getTemplateUpdateDelayMilliseconds();
 
     /**
      * Tells if this setting was explicitly set (otherwise its value will be 
the default value).
@@ -158,8 +186,8 @@ public interface TopLevelConfiguration extends 
ParsingAndProcessingConfiguration
     Version getIncompatibleImprovements();
 
     /**
-     * Whether localized template lookup is enabled. Enabled by default.
-     *
+     * Whether localized template lookup is enabled . The default is {@code 
true}, except when the
+     * {@link #getTemplateResolver() templateResolver} doesn't support this 
setting, in which case it's {@code null}.
      * <p>
      * With the default {@link TemplateLookupStrategy}, localized lookup works 
like this: Let's say your locale setting
      * is {@code Locale("en", "AU")}, and you call {@link 
Configuration#getTemplate(String) cfg.getTemplate("foo.ftl")}.
@@ -168,8 +196,11 @@ public interface TopLevelConfiguration extends 
ParsingAndProcessingConfiguration
      * {@link #getTemplateLookupStrategy()} for a more details. If you need to 
generate different
      * template names, set your own a {@link TemplateLookupStrategy} 
implementation as the value of the
      * {@link #getTemplateLookupStrategy() templateLookupStrategy} setting.
+     * <p>
+     * If the {@link #getTemplateResolver() templateResolver} doesn't support 
this setting, then it must be {@code
+     * null}. This check is postponed until the {@link Configuration} instance 
is created.
      */
-    boolean getLocalizedLookup();
+    Boolean getLocalizedLookup();
 
     /**
      * Tells if this setting was explicitly set (otherwise its value will be 
the default value).

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactory.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactory.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactory.java
index e61bb23..b78f49b 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactory.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateConfigurationFactory.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateConfiguration;
+import org.apache.freemarker.core.TopLevelConfiguration;
 
 /**
  * Creates (or returns) {@link TemplateConfiguration}-s for template sources.
@@ -35,7 +36,10 @@ public abstract class TemplateConfigurationFactory {
      *            The name (path) that was used for {@link 
TemplateLoader#load}. See
      *            {@link Template#getSourceName()} for details.
      * @param templateLoadingSource
-     *            The object returned by {@link 
TemplateLoadingResult#getSource()}.
+     *            The object returned by {@link 
TemplateLoadingResult#getSource()}. For a
+     *            {@link TopLevelConfiguration#getTemplateResolver()} that 
doesn't use {@link TemplateLoader}-s
+     *            this is maybe {@code null}, or some other object wrapped 
into {@link TemplateLoadingSource} to
+     *            satisfy this contract.
      * 
      * @return The {@link TemplateConfiguration} to apply, or {@code null} if 
the there's no {@link TemplateConfiguration} for
      *         this template source.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingSource.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingSource.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingSource.java
index bfe47e4..5cb5e34 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingSource.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateLoadingSource.java
@@ -44,7 +44,7 @@ import 
org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader;
  * 
  * <li>{@link Object#equals(Object)} must still work properly if there are 
multiple instances of the same
  * {@link TemplateLoader} implementation. Like if you have an implementation 
that loads from a database table, the
- * {@link TemplateLoadingSource} should certain contain the JDBC connection 
string, the table name and the row ID, not
+ * {@link TemplateLoadingSource} should certainly contain the JDBC connection 
string, the table name and the row ID, not
  * just the row ID.
  * 
  * <li>Together with {@link Object#equals(Object)}, {@link Object#hashCode()} 
must be also overridden. The template

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java
index c2c5c61..de70101 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolver.java
@@ -23,45 +23,93 @@ import java.io.Serializable;
 import java.util.Locale;
 
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.ConfigurationException;
 import org.apache.freemarker.core.ParseException;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateNotFoundException;
 import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateResolver;
+import org.apache.freemarker.core.util._NullArgumentException;
 
 /**
- * This class was introduced to allow users to fully implement the template 
lookup, loading and caching logic,
- * in case the standard mechanism ({@link DefaultTemplateResolver}) is not 
flexible enough. By implementing this class,
- * you can take over the duty of the following {@link Configuration} settings, 
and it's up to the implementation if you
- * delegate some of those duties back to the {@link Configuration} setting:
- * 
- * <ul>
- * <li>{@link Configuration#getTemplateLoader() templateLoader}
- * <li>{@link Configuration#getTemplateNameFormat() templateNameFormat}
- * <li>{@link Configuration#getTemplateLookupStrategy() templateLookupStrategy}
- * <li>{@link Configuration#getCacheStorage() cacheStorage}
- * </ul>
+ * This class allows user to fully implement the template lookup, loading and 
caching logic,
+ * in case the standard mechanism (a {@link DefaultTemplateResolver} combined 
with all the {@link Configuration}
+ * settings like {@link Configuration#getTemplateLoader() templateLoader},
+ * {@link Configuration#getTemplateConfigurations() templateConfigurations}, 
etc.) is not flexible enough.
+ * <p>
+ * A custom {@link TemplateResolver} can depend on a selected set of {@link 
Configuration} settings; the same ones that
+ * {@link DefaultTemplateResolver} depends on. These settings are collected 
into the
+ * {@link TemplateResolverDependencies} class, and the {@link 
TemplateResolver} should get them in {@link #initialize()}
+ * via {@link #getDependencies()}. It's possible that the custom {@link 
TemplateResolver} only uses some of these
+ * settings, which should be reflected by the return value of the {@code 
supportsXxxDependency} methods (like
+ * {@link #supportsTemplateLoaderSetting()}). (Note that there's no {@code 
supportsXxxDependency} method for
+ * {@link Configuration#getTemplateLanguage() templateLanguage} and {@link 
Configuration#getSourceEncoding()
+ * sourceEncoding} and these settings are always exposed.) {@link 
TemplateResolverDependencies} will also expose the
+ * {@link TemplateResolverDependencies#parse} method, which is used to create 
a {@link Template} from its source code.
  */
-//TODO DRAFT only [FM3]
 public abstract class TemplateResolver {
 
-    private final Configuration configuration;
+    private TemplateResolverDependencies dependencies;
+    private boolean initialized;
 
-    protected TemplateResolver(Configuration configuration) {
-        this.configuration = configuration;
+    /**
+     * Called by FreeMarker when the {@link Configuration} is built; normally 
you do not call it yourself.
+     * This automatically calls {@link #initialize()}.
+     *
+     * @throws IllegalStateException If this method was already called with 
another {@link Configuration} instance.
+     */
+    public final void setDependencies(TemplateResolverDependencies 
dependencies) {
+        _NullArgumentException.check("dependencies", dependencies);
+        synchronized (this) {
+            // Note that this doesn't make the TemplateResolver safely 
published (as par Java Memory Model). It only
+            // guarantees that the configuration won't be changed once it was 
set.
+            if (this.dependencies != null) {
+                if (this.dependencies == dependencies) {
+                    return;
+                }
+                throw new IllegalStateException(
+                        "This TemplateResolver is already bound to another 
Configuration instance.");
+            }
+            this.dependencies = dependencies;
+
+            initialize();
+
+            initialized = true;
+        } // sync.
     }
 
-    public Configuration getConfiguration() {
-        return configuration;
+    /**
+     * Returns {@code null} before {@link #initialize()}
+     */
+    protected TemplateResolverDependencies getDependencies() {
+        return dependencies;
+    }
+
+    /**
+     * You meant to initialize the instance here instead of in the 
constructor. This is called only once (by
+     * FreeMarker at least), when the {@link 
#setDependencies(TemplateResolverDependencies)}  dependencies} is called.
+     */
+    protected abstract void initialize() throws ConfigurationException;
+
+    /**
+     * Checks if the {@link TemplateResolver} was fully initialized.
+     * It's a good practice to call this at the beginning of most method.
+     */
+    protected void checkInitialized() {
+        if (!initialized) {
+            throw new IllegalStateException(
+                    "TemplateResolver wasn't properly initialized; ensure that 
initialize() was called and did not "
+                    + "throw an exception.");
+        }
     }
 
     /**
      * Retrieves the parsed template with the given name (and according the 
specified further parameters), or returns a
      * result that indicates that no such template exists. The result should 
come from a cache most of the time
      * (avoiding I/O and template parsing), as this method is typically called 
frequently.
-     * 
      * <p>
      * All parameters must be non-{@code null}, except {@code 
customLookupCondition}. For the meaning of the parameters
-     * see {@link Configuration#getTemplate(String, Locale, Serializable, 
boolean)}.
+     * see {@link Configuration#getTemplate(String, Locale, Serializable, 
boolean)}. Note that {@code name} parameter
+     * is not normalized; you are supposed to call {@link 
#normalizeRootBasedName(String)} internally.
      *
      * @return A {@link GetTemplateResult} object that contains the {@link 
Template}, or a
      *         {@link GetTemplateResult} object that contains {@code null} as 
the {@link Template} and information
@@ -80,18 +128,15 @@ public abstract class TemplateResolver {
      *             should never be a {@link TemplateNotFoundException}, as 
that condition is indicated in the return
      *             value.
      */
-    // [FM3] This parameters will be removed: String encoding
     public abstract GetTemplateResult getTemplate(String name, Locale locale, 
Serializable customLookupCondition)
             throws MalformedTemplateNameException, ParseException, IOException;
 
     /**
      * Clears the cache of templates, to enforce re-loading templates when 
they are get next time; this is an optional
      * operation.
-     * 
      * <p>
      * Note that if the {@link TemplateResolver} implementation uses {@link 
TemplateLoader}-s, it should also call
      * {@link TemplateLoader#resetState()} on them.
-     * 
      * <p>
      * This method is thread-safe and can be called while the engine processes 
templates.
      * 
@@ -104,10 +149,8 @@ public abstract class TemplateResolver {
      * Removes a template from the template cache, hence forcing the 
re-loading of it when it's next time requested;
      * this is an optional operation. This is to give the application finer 
control over cache updating than the
      * {@link Configuration#getTemplateUpdateDelayMilliseconds() 
templateUpdateDelayMilliseconds} setting alone gives.
-     * 
      * <p>
      * For the meaning of the parameters, see {@link #getTemplate(String, 
Locale, Serializable)}
-     * 
      * <p>
      * This method is thread-safe and can be called while the engine processes 
templates.
      * 
@@ -121,10 +164,9 @@ public abstract class TemplateResolver {
      * Converts a name to a template root directory based name, so that it can 
be used to find a template without
      * knowing what (like which template) has referred to it. The rules depend 
on the name format, but a typical example
      * is converting "t.ftl" with base "sub/contex.ftl" to "sub/t.ftl".
-     * 
      * <p>
-     * Some implementations, notably {@link DefaultTemplateResolver}, 
delegates this check to the
-     * {@link TemplateNameFormat} coming from the {@link Configuration}.
+     * Some implementations, notably {@link DefaultTemplateResolver}, delegate 
this task to a
+     * {@link TemplateNameFormat}.
      * 
      * @param baseName
      *            Maybe a file name, maybe a directory name. The meaning of 
file name VS directory name depends on the
@@ -147,12 +189,11 @@ public abstract class TemplateResolver {
      * Normalizes a template root directory based name (relative to the root 
or absolute), so that equivalent names
      * become equivalent according {@link String#equals(Object)} too. The 
rules depend on the name format, but typical
      * examples are "sub/../t.ftl" to "t.ftl", "sub/./t.ftl" to "sub/t.ftl" 
and "/t.ftl" to "t.ftl".
-     * 
      * <p>
-     * Some implementations, notably {@link DefaultTemplateResolver}, 
delegates this check to the {@link TemplateNameFormat}
-     * coming from the {@link Configuration}. The standard {@link 
TemplateNameFormat} implementations shipped with
-     * FreeMarker always returns a root relative path (except if the name 
starts with an URI schema, in which case a
-     * full URI is returned), for example, "/foo.ftl" becomes to "foo.ftl".
+     * Some implementations, notably {@link DefaultTemplateResolver}, 
delegates this check to a
+     * {@link TemplateNameFormat}. The standard {@link TemplateNameFormat} 
implementations shipped with FreeMarker
+     * always return a root relative path (except if the name starts with an 
URI schema, in which case a full URI is
+     * returned), for example, "/foo.ftl" becomes to "foo.ftl".
      * 
      * @param name
      *            The root based name. Not {@code null}.
@@ -161,4 +202,53 @@ public abstract class TemplateResolver {
      */
     public abstract String normalizeRootBasedName(String name) throws 
MalformedTemplateNameException;
 
+    /**
+     * Tells whether the {@link TemplateResolver} implementation depends on the
+     * {@link Configuration#getTemplateLoader() templateLoader} {@link 
Configuration}. If it returns {@code false}
+     * then this {@link TemplateResolver} must not call {@link 
TemplateResolverDependencies#getTemplateLoader()}, or
+     * else that will throw {@link IllegalStateException}. Furthermore if the 
user sets the {@code templateLoader} in
+     * the {@link Configuration} to non-{@code null} value (the default is 
{@code null}), then the
+     * {@link Configuration} constructor will throw an exception to tell the 
user that the {@code templateLoader}
+     * setting is not supported by this {@link TemplateResolver} class. Some 
may feel tempted to return {@code true}
+     * to avoid such error, but consider that as the user has explicitly set 
this setting, they certainly expect it
+     * have an effect, and will be frustrated when its ignored.
+     */
+    public abstract boolean supportsTemplateLoaderSetting();
+
+    /**
+     * Works like {@link #supportsTemplateLoaderSetting()}, but for the
+     * {@link Configuration#getCacheStorage() cacheStorage} setting.
+     */
+    public abstract boolean supportsCacheStorageSetting();
+
+    /**
+     * Works like {@link #supportsTemplateLoaderSetting()}, but for the
+     * {@link Configuration#getTemplateLookupStrategy() 
templateLookupStrategy} setting.
+     */
+    public abstract boolean supportsTemplateLookupStrategySetting();
+
+    /**
+     * Works like {@link #supportsTemplateLoaderSetting()}, but for the
+     * {@link Configuration#getTemplateNameFormat() templateNameFormat} 
setting.
+     */
+    public abstract boolean supportsTemplateNameFormatSetting();
+
+    /**
+     * Works like {@link #supportsTemplateLoaderSetting()}, but for the
+     * {@link Configuration#getTemplateConfigurations() 
templateConfigurations} setting.
+     */
+    public abstract boolean supportsTemplateConfigurationsSetting();
+
+    /**
+     * Works like {@link #supportsTemplateUpdateDelayMillisecondsSetting()}, 
but for the
+     * {@link Configuration#getTemplateUpdateDelayMilliseconds() 
templateUpdateDelayMilliseconds} setting.
+     */
+    public abstract boolean supportsTemplateUpdateDelayMillisecondsSetting();
+
+    /**
+     * Works like {@link #supportsTemplateLoaderSetting()}, but for the
+     * {@link Configuration#getLocalizedLookup() localizedLookup} setting.
+     */
+    public abstract boolean supportsLocalizedLookupSetting();
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolverDependencies.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolverDependencies.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolverDependencies.java
new file mode 100644
index 0000000..13820c3
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/TemplateResolverDependencies.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.templateresolver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.ParseException;
+import org.apache.freemarker.core.Template;
+import org.apache.freemarker.core.TemplateConfiguration;
+import org.apache.freemarker.core.TemplateLanguage;
+
+/**
+ * Stores the dependencies of a {@link TemplateResolver}. See {@link 
TemplateResolver} for more.
+ * This is normally implemented by FreeMarker internally.
+ */
+public abstract class TemplateResolverDependencies {
+
+    /**
+     * @throws IllegalStateException if {@link 
TemplateResolver#supportsTemplateLoaderSetting()} return {@code false}.
+     */
+    public abstract TemplateLoader getTemplateLoader();
+
+    /**
+     * @throws IllegalStateException if {@link 
TemplateResolver#supportsCacheStorageSetting()} return {@code false}.
+     */
+    public abstract CacheStorage getCacheStorage();
+
+    /**
+     * @throws IllegalStateException if {@link 
TemplateResolver#supportsTemplateLookupStrategySetting()} return
+     * {@code false}.
+     */
+    public abstract TemplateLookupStrategy getTemplateLookupStrategy();
+
+    /**
+     * @throws IllegalStateException if {@link 
TemplateResolver#supportsTemplateNameFormatSetting()} return
+     * {@code false}.
+     */
+    public abstract TemplateNameFormat getTemplateNameFormat();
+
+    /**
+     * @throws IllegalStateException if {@link 
TemplateResolver#supportsTemplateConfigurationsSetting()}
+     * return {@code false}.
+     */
+    public abstract TemplateConfigurationFactory getTemplateConfigurations();
+
+    /**
+     * @throws IllegalStateException if {@link 
TemplateResolver#supportsTemplateUpdateDelayMillisecondsSetting()}
+     * return {@code false}.
+     */
+    public abstract Long getTemplateUpdateDelayMilliseconds();
+
+    /**
+     * @throws IllegalStateException if {@link 
TemplateResolver#supportsLocalizedLookupSetting()} return {@code false}.
+     */
+    public abstract Boolean getLocalizedLookup();
+
+    public abstract Charset getSourceEncoding();
+
+    public abstract TemplateLanguage getTemplateLanguage();
+
+    /**
+     * This simply calls {@link TemplateLanguage#parse(String, String, Reader, 
Configuration, TemplateConfiguration,
+     * Charset, InputStream)} without exposing the {@link Configuration}.
+     */
+    public abstract Template parse(
+            TemplateLanguage templateLanguage, String name, String sourceName, 
Reader reader,
+            TemplateConfiguration templateConfiguration, Charset encoding,
+            InputStream streamToUnmarkWhenEncEstabd) throws IOException, 
ParseException;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/_CacheAPI.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/_CacheAPI.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/_CacheAPI.java
deleted file mode 100644
index 09b216f..0000000
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/_CacheAPI.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.freemarker.core.templateresolver;
-
-/**
- * For internal use only; don't depend on this, there's no backward 
compatibility guarantee at all!
- * This class is to work around the lack of module system in Java, i.e., so 
that other FreeMarker packages can
- * access things inside this package that users shouldn't. 
- */ 
-public final class _CacheAPI {
-
-    private _CacheAPI() {
-        // Not meant to be instantiated
-    }
-    
-    public static String toRootBasedName(TemplateNameFormat 
templateNameFormat, String baseName, String targetName)
-            throws MalformedTemplateNameException {
-        return templateNameFormat.toRootBasedName(baseName, targetName);
-    }
-
-    public static String normalizeRootBasedName(TemplateNameFormat 
templateNameFormat, String name)
-            throws MalformedTemplateNameException {
-        return templateNameFormat.normalizeRootBasedName(name);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/8915ac9b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
index 400693e..a4ac768 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/templateresolver/impl/DefaultTemplateResolver.java
@@ -19,6 +19,8 @@
 
 package org.apache.freemarker.core.templateresolver.impl;
 
+import static org.apache.freemarker.core.Configuration.ExtendableBuilder.*;
+
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -33,6 +35,8 @@ import java.util.Locale;
 import java.util.StringTokenizer;
 
 import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.ConfigurationException;
+import org.apache.freemarker.core.ConfigurationSettingValueException;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateConfiguration;
 import org.apache.freemarker.core.TemplateLanguage;
@@ -52,6 +56,7 @@ import 
org.apache.freemarker.core.templateresolver.TemplateLoadingSource;
 import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy;
 import org.apache.freemarker.core.templateresolver.TemplateNameFormat;
 import org.apache.freemarker.core.templateresolver.TemplateResolver;
+import 
org.apache.freemarker.core.templateresolver.TemplateResolverDependencies;
 import org.apache.freemarker.core.util.BugException;
 import org.apache.freemarker.core.util.UndeclaredThrowableException;
 import org.apache.freemarker.core.util._NullArgumentException;
@@ -83,100 +88,58 @@ public class DefaultTemplateResolver extends 
TemplateResolver {
     private static final Logger LOG = _CoreLogs.TEMPLATE_RESOLVER;
 
     /** Maybe {@code null}. */
-    private final TemplateLoader templateLoader;
+    private TemplateLoader templateLoader;
     
     /** Here we keep our cached templates */
-    private final CacheStorage cacheStorage;
-    private final TemplateLookupStrategy templateLookupStrategy;
-    private final TemplateNameFormat templateNameFormat;
-    private final TemplateConfigurationFactory templateConfigurations;
-    private final long templateUpdateDelayMilliseconds;
-    private final boolean localizedLookup;
-
-    private Configuration config;
-    
-    /**
-     * @param templateLoader
-     *            The {@link TemplateLoader} to use. Can be {@code null}, 
though then every request will result in
-     *            {@link TemplateNotFoundException}.
-     * @param cacheStorage
-     *            The {@link CacheStorage} to use. Can't be {@code null}.
-     * @param templateLookupStrategy
-     *            The {@link TemplateLookupStrategy} to use. Can't be {@code 
null}.
-     * @param templateUpdateDelayMilliseconds
-     *            See {@link 
Configuration#getTemplateUpdateDelayMilliseconds()}
-     * @param templateNameFormat
-     *            The {@link TemplateNameFormat} to use. Can't be {@code null}.
-     * @param templateConfigurations
-     *            The {@link TemplateConfigurationFactory} to use. Can be 
{@code null} (then all templates will use the
-     *            settings coming from the {@link Configuration} as is, except 
in the very rare case where a
-     *            {@link TemplateLoader} itself specifies a {@link 
TemplateConfiguration}).
-     * @param config
-     *            The {@link Configuration} this cache will be used for. Can't 
be {@code null}.
-     */
-    public DefaultTemplateResolver(
-            TemplateLoader templateLoader,
-            CacheStorage cacheStorage, long templateUpdateDelayMilliseconds,
-            TemplateLookupStrategy templateLookupStrategy, boolean 
localizedLookup,
-            TemplateNameFormat templateNameFormat,
-            TemplateConfigurationFactory templateConfigurations,
-            Configuration config) {
-        super(config);
-        
-        this.templateLoader = templateLoader;
-        
-        _NullArgumentException.check("cacheStorage", cacheStorage);
-        this.cacheStorage = cacheStorage;
+    private CacheStorage cacheStorage;
+    private TemplateLookupStrategy templateLookupStrategy;
+    private TemplateNameFormat templateNameFormat;
+    private TemplateConfigurationFactory templateConfigurations;
+    private long templateUpdateDelayMilliseconds;
+    private Charset sourceEncoding;
+    private TemplateLanguage templateLanguage;
+    private boolean localizedLookup;
+
+    @Override
+    protected void initialize() throws ConfigurationException {
+        TemplateResolverDependencies deps = getDependencies();
+
+        this.templateLoader = deps.getTemplateLoader();
         
+        this.cacheStorage = deps.getCacheStorage();
+        checkDependencyNotNull(CACHE_STORAGE_KEY, this.cacheStorage);
+
+        Long templateUpdateDelayMilliseconds = 
deps.getTemplateUpdateDelayMilliseconds();
+        checkDependencyNotNull(TEMPLATE_UPDATE_DELAY_KEY, 
templateUpdateDelayMilliseconds);
         this.templateUpdateDelayMilliseconds = templateUpdateDelayMilliseconds;
-        
+
+        Boolean localizedLookup = deps.getLocalizedLookup();
+        checkDependencyNotNull(LOCALIZED_LOOKUP_KEY, localizedLookup);
         this.localizedLookup = localizedLookup;
-        
-        _NullArgumentException.check("templateLookupStrategy", 
templateLookupStrategy);
-        this.templateLookupStrategy = templateLookupStrategy;
 
-        _NullArgumentException.check("templateNameFormat", templateNameFormat);
-        this.templateNameFormat = templateNameFormat;
+        this.templateLookupStrategy = deps.getTemplateLookupStrategy();
+        checkDependencyNotNull(TEMPLATE_LOOKUP_STRATEGY_KEY, 
this.templateLookupStrategy);
+
+        this.templateNameFormat = deps.getTemplateNameFormat();
+        checkDependencyNotNull(TEMPLATE_NAME_FORMAT_KEY, 
this.templateNameFormat);
 
         // Can be null
-        this.templateConfigurations = templateConfigurations;
-        
-        _NullArgumentException.check("config", config);
-        this.config = config;
-    }
-    
-    /**
-     * Returns the configuration for internal usage.
-     */
-    @Override
-    public Configuration getConfiguration() {
-        return config;
-    }
+        this.templateConfigurations = deps.getTemplateConfigurations();
 
-    public TemplateLoader getTemplateLoader() {
-        return templateLoader;
-    }
+        this.sourceEncoding = deps.getSourceEncoding();
+        checkDependencyNotNull(SOURCE_ENCODING_KEY, this.sourceEncoding);
 
-    public CacheStorage getCacheStorage() {
-        return cacheStorage;
+        this.templateLanguage = deps.getTemplateLanguage();
+        checkDependencyNotNull(TEMPLATE_LANGUAGE_KEY, this.templateLanguage);
     }
-    
-    /**
-     */
-    public TemplateLookupStrategy getTemplateLookupStrategy() {
-        return templateLookupStrategy;
-    }
-    
-    /**
-     */
-    public TemplateNameFormat getTemplateNameFormat() {
-        return templateNameFormat;
-    }
-    
-    /**
-     */
-    public TemplateConfigurationFactory getTemplateConfigurations() {
-        return templateConfigurations;
+
+    private void checkDependencyNotNull(String name, Object value) {
+        if (value == null) {
+            throw new ConfigurationSettingValueException(
+                    name, null, false,
+                    "This Configuration setting must be set and non-null when 
the TemplateResolver is a(n) "
+                    + this.getClass().getName() + ".", null);
+        }
     }
 
     /**
@@ -213,6 +176,8 @@ public class DefaultTemplateResolver extends 
TemplateResolver {
         _NullArgumentException.check("name", name);
         _NullArgumentException.check("locale", locale);
 
+        checkInitialized();
+
         name = templateNameFormat.normalizeRootBasedName(name);
         
         if (templateLoader == null) {
@@ -233,6 +198,41 @@ public class DefaultTemplateResolver extends 
TemplateResolver {
         return templateNameFormat.normalizeRootBasedName(name);
     }
 
+    @Override
+    public boolean supportsTemplateLoaderSetting() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsCacheStorageSetting() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsTemplateLookupStrategySetting() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsTemplateNameFormatSetting() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsTemplateConfigurationsSetting() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsTemplateUpdateDelayMillisecondsSetting() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsLocalizedLookupSetting() {
+        return true;
+    }
+
     private Template getTemplateInternal(
             final String name, final Locale locale, final Serializable 
customLookupCondition)
     throws IOException {
@@ -533,9 +533,9 @@ public class DefaultTemplateResolver extends 
TemplateResolver {
             locale = tc.getLocale();
         }
         Charset initialEncoding = tc != null && tc.isSourceEncodingSet() ? 
tc.getSourceEncoding()
-                : config.getSourceEncoding();
+                : this.sourceEncoding;
         TemplateLanguage templateLanguage = tc != null && 
tc.isTemplateLanguageSet() ? tc.getTemplateLanguage()
-                : config.getTemplateLanguage();
+                : this.templateLanguage;
 
         Template template;
         {
@@ -572,7 +572,8 @@ public class DefaultTemplateResolver extends 
TemplateResolver {
             
             try {
                 try {
-                    template = templateLanguage.parse(name, sourceName, 
reader, config, tc,
+                    template = getDependencies().parse(
+                            templateLanguage, name, sourceName, reader, tc,
                             initialEncoding, markedInputStream);
                 } catch (WrongTemplateCharsetException charsetException) {
                     final Charset templateSpecifiedEncoding = 
charsetException.getTemplateSpecifiedEncoding();
@@ -590,7 +591,8 @@ public class DefaultTemplateResolver extends 
TemplateResolver {
                                 + ", but its canSpecifyCharsetInContent 
property is false.");
                     }
 
-                    template = templateLanguage.parse(name, sourceName, 
reader, config, tc,
+                    template = getDependencies().parse(
+                            templateLanguage, name, sourceName, reader, tc,
                             templateSpecifiedEncoding, markedInputStream);
                 }
             } finally {
@@ -604,28 +606,6 @@ public class DefaultTemplateResolver extends 
TemplateResolver {
     }
 
     /**
-     * Gets the delay in milliseconds between checking for newer versions of a
-     * template source.
-     * @return the current value of the delay
-     */
-    public long getTemplateUpdateDelayMilliseconds() {
-        // synchronized was moved here so that we don't advertise that it's 
thread-safe, as it's not.
-        synchronized (this) {
-            return templateUpdateDelayMilliseconds;
-        }
-    }
-
-    /**
-     * Returns if localized template lookup is enabled or not.
-     */
-    public boolean getLocalizedLookup() {
-        // synchronized was moved here so that we don't advertise that it's 
thread-safe, as it's not.
-        synchronized (this) {
-            return localizedLookup;
-        }
-    }
-
-    /**
      * Removes all entries from the cache, forcing reloading of templates on 
subsequent
      * {@link #getTemplate(String, Locale, Serializable)} calls.
      * 
@@ -654,14 +634,6 @@ public class DefaultTemplateResolver extends 
TemplateResolver {
         }
     }
 
-    /**
-     * Removes an entry from the cache, hence forcing the re-loading of it 
when it's next time requested. (It doesn't
-     * delete the template file itself.) This is to give the application finer 
control over cache updating than the
-     * update delay ({@link #getTemplateUpdateDelayMilliseconds()}) alone does.
-     * 
-     * For the meaning of the parameters, see
-     * {@link Configuration#getTemplate(String, Locale, Serializable, boolean)}
-     */
     @Override
     public void removeTemplateFromCache(
             String name, Locale locale, Serializable customLookupCondition)

Reply via email to