http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/DefaultTemplateResolver.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/templateresolver/DefaultTemplateResolver.java
 
b/src/main/java/org/apache/freemarker/core/templateresolver/DefaultTemplateResolver.java
deleted file mode 100644
index e6b9ea1..0000000
--- 
a/src/main/java/org/apache/freemarker/core/templateresolver/DefaultTemplateResolver.java
+++ /dev/null
@@ -1,1028 +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;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.Serializable;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.StringTokenizer;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.Template;
-import org.apache.freemarker.core.TemplateNotFoundException;
-import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core._TemplateAPI;
-import org.apache.freemarker.core.ast.BugException;
-import 
org.apache.freemarker.core.ast.MarkReleaserTemplateSpecifiedEncodingHandler;
-import org.apache.freemarker.core.ast.TemplateConfiguration;
-import org.apache.freemarker.core.ast.TemplateSpecifiedEncodingHandler;
-import org.apache.freemarker.core.util.NullArgumentException;
-import org.apache.freemarker.core.util.StringUtil;
-import org.apache.freemarker.core.util.UndeclaredThrowableException;
-import org.slf4j.Logger;
-
-/**
- * Performs caching and on-demand loading of the templates.
- * The actual template "file" loading is delegated to a {@link TemplateLoader} 
that you can specify in the constructor.
- * Some aspects of caching is delegated to a {@link CacheStorage} that you can 
also specify in the constructor.
- * 
- * <p>Typically you don't instantiate or otherwise use this class directly. 
The {@link Configuration} embeds an
- * instance of this class, that you access indirectly through {@link 
Configuration#getTemplate(String)} and other
- * {@link Configuration} API-s. Then {@link TemplateLoader} and {@link 
CacheStorage} can be set with
- * {@link Configuration#setTemplateLoader(TemplateLoader)} and
- * {@link Configuration#setCacheStorage(CacheStorage)}.
- */
-public class DefaultTemplateResolver extends TemplateResolver {
-    
-    /**
-     * The default template update delay; see {@link 
Configuration#setTemplateUpdateDelayMilliseconds(long)}.
-     * 
-     * @since 2.3.23
-     */
-    public static final long DEFAULT_TEMPLATE_UPDATE_DELAY_MILLIS = 5000L;
-    
-    private static final String ASTERISKSTR = "*";
-    private static final char ASTERISK = '*';
-    private static final char SLASH = '/';
-    private static final String LOCALE_PART_SEPARATOR = "_";
-    private static final Logger LOG = _CoreLogs.TEMPLATE_RESOLVER;
-
-    /** Maybe {@code null}. */
-    private final 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 boolean isCacheStorageConcurrent;
-    /** {@link Configuration#setTemplateUpdateDelayMilliseconds(long)} */
-    private long templateUpdateDelayMilliseconds = 
DEFAULT_TEMPLATE_UPDATE_DELAY_MILLIS;
-    /** {@link Configuration#setLocalizedLookup(boolean)} */
-    private boolean localizedLookup = true;
-
-    private Configuration config;
-
-    /**
-     * Same as {@link #DefaultTemplateResolver(TemplateLoader, CacheStorage, 
Configuration)} with a new {@link SoftCacheStorage}
-     * as the 2nd parameter.
-     * 
-     * @since 2.3.21
-     */
-    public DefaultTemplateResolver(TemplateLoader templateLoader, 
Configuration config) {
-        this(templateLoader, 
_TemplateAPI.createDefaultCacheStorage(Configuration.VERSION_2_3_0), config);
-    }
-    
-    /**
-     * Same as
-     * {@link #DefaultTemplateResolver(TemplateLoader, CacheStorage, 
TemplateLookupStrategy, TemplateNameFormat, Configuration)}
-     * with {@link TemplateLookupStrategy#DEFAULT_2_3_0} and {@link 
TemplateNameFormat#DEFAULT_2_3_0}.
-     * 
-     * @since 2.3.21
-     */
-    public DefaultTemplateResolver(TemplateLoader templateLoader, CacheStorage 
cacheStorage, Configuration config) {
-        this(templateLoader, cacheStorage,
-                
_TemplateAPI.getDefaultTemplateLookupStrategy(Configuration.VERSION_2_3_0),
-                
_TemplateAPI.getDefaultTemplateNameFormat(Configuration.VERSION_2_3_0),
-                config);
-    }
-    
-    /**
-     * Same as
-     * {@link #DefaultTemplateResolver(TemplateLoader, CacheStorage, 
TemplateLookupStrategy, TemplateNameFormat,
-     * TemplateConfigurationFactory, Configuration)} with {@code null} for 
{@code templateConfigurations}-s.
-     * 
-     * @since 2.3.22
-     */
-    public DefaultTemplateResolver(TemplateLoader templateLoader, CacheStorage 
cacheStorage,
-            TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat 
templateNameFormat,
-            Configuration config) {
-        this(templateLoader, cacheStorage, templateLookupStrategy, 
templateNameFormat, null, 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 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}.
-     * 
-     * @since 2.3.24
-     */
-    public DefaultTemplateResolver(TemplateLoader templateLoader, CacheStorage 
cacheStorage,
-            TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat 
templateNameFormat,
-            TemplateConfigurationFactory templateConfigurations,
-            Configuration config) {
-        super(config);
-        
-        this.templateLoader = templateLoader;
-        
-        NullArgumentException.check("cacheStorage", cacheStorage);
-        this.cacheStorage = cacheStorage;
-        isCacheStorageConcurrent = cacheStorage instanceof 
ConcurrentCacheStorage &&
-                ((ConcurrentCacheStorage) cacheStorage).isConcurrent();
-        
-        NullArgumentException.check("templateLookupStrategy", 
templateLookupStrategy);
-        this.templateLookupStrategy = templateLookupStrategy;
-
-        NullArgumentException.check("templateNameFormat", templateNameFormat);
-        this.templateNameFormat = 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;
-    }
-
-    public TemplateLoader getTemplateLoader() {
-        return templateLoader;
-    }
-
-    public CacheStorage getCacheStorage() {
-        return cacheStorage;
-    }
-    
-    /**
-     * @since 2.3.22
-     */
-    public TemplateLookupStrategy getTemplateLookupStrategy() {
-        return templateLookupStrategy;
-    }
-    
-    /**
-     * @since 2.3.22
-     */
-    public TemplateNameFormat getTemplateNameFormat() {
-        return templateNameFormat;
-    }
-    
-    /**
-     * @since 2.3.24
-     */
-    public TemplateConfigurationFactory getTemplateConfigurations() {
-        return templateConfigurations;
-    }
-
-    /**
-     * Retrieves the template with the given name (and according the specified 
further parameters) from the template
-     * cache, loading it into the cache first if it's missing/staled.
-     * 
-     * <p>
-     * All parameters must be non-{@code null}, except {@code 
customLookupCondition}. For the meaning of the parameters
-     * see {@link Configuration#getTemplate(String, Locale, String, boolean)}.
-     *
-     * @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
-     *         about the missing template. The return value itself is never 
{@code null}. Note that exceptions occurring
-     *         during template loading will not be classified as a missing 
template, so they will cause an exception to
-     *         be thrown by this method instead of returning a {@link 
GetTemplateResult}. The idea is that having a
-     *         missing template is normal (not exceptional), providing that 
the backing storage mechanism could indeed
-     *         check that it's missing.
-     * 
-     * @throws MalformedTemplateNameException
-     *             If the {@code name} was malformed according the current 
{@link TemplateNameFormat}. However, if the
-     *             {@link TemplateNameFormat} is {@link 
TemplateNameFormat#DEFAULT_2_3_0} and
-     *             {@link Configuration#getIncompatibleImprovements()} is less 
than 2.4.0, then instead of throwing this
-     *             exception, a {@link GetTemplateResult} will be returned, 
similarly as if the template were missing
-     *             (the {@link GetTemplateResult#getMissingTemplateReason()} 
will describe the real error).
-     * 
-     * @throws IOException
-     *             If reading the template has failed from a reason other than 
the template is missing. This method
-     *             should never be a {@link TemplateNotFoundException}, as 
that condition is indicated in the return
-     *             value.
-     * 
-     * @since 2.3.22
-     */
-    @Override
-    public GetTemplateResult getTemplate(String name, Locale locale, Object 
customLookupCondition,
-            String encoding, boolean parseAsFTL)
-    throws IOException {
-        NullArgumentException.check("name", name);
-        NullArgumentException.check("locale", locale);
-        NullArgumentException.check("encoding", encoding);
-        
-        try {
-            name = templateNameFormat.normalizeRootBasedName(name);
-        } catch (MalformedTemplateNameException e) {
-            // If we don't have to emulate backward compatible behavior, then 
just rethrow it: 
-            if (templateNameFormat != TemplateNameFormat.DEFAULT_2_3_0
-                    || config.getIncompatibleImprovements().intValue() >= 
_TemplateAPI.VERSION_INT_2_4_0) {
-                throw e;
-            }
-            return new GetTemplateResult(null, e);
-        }
-        
-        if (templateLoader == null) {
-            return new GetTemplateResult(name, "The TemplateLoader (and 
TemplateLoader2) was null.");
-        }
-        
-        Template template = getTemplateInternal(name, locale, 
customLookupCondition, encoding, parseAsFTL);
-        return template != null ? new GetTemplateResult(template) : new 
GetTemplateResult(name, (String) null);
-    }
-
-    private Template getTemplateInternal(
-            final String name, final Locale locale, final Object 
customLookupCondition,
-            final String encoding, final boolean parseAsFTL)
-    throws IOException {
-        final boolean debug = LOG.isDebugEnabled();
-        final String debugPrefix = debug
-                ? getDebugPrefix("getTemplate", name, locale, 
customLookupCondition, encoding, parseAsFTL)
-                : null;
-        final CachedResultKey cacheKey = new CachedResultKey(name, locale, 
customLookupCondition, encoding, parseAsFTL);
-        
-        CachedResult oldCachedResult;
-        if (isCacheStorageConcurrent) {
-            oldCachedResult = (CachedResult) cacheStorage.get(cacheKey);
-        } else {
-            synchronized (cacheStorage) {
-                oldCachedResult = (CachedResult) cacheStorage.get(cacheKey);
-            }
-        }
-        
-        final long now = System.currentTimeMillis();
-        
-        boolean rethrownCachedException = false;
-        boolean suppressFinallyException = false;
-        TemplateLookupResult newLookupResult = null;
-        CachedResult newCachedResult = null;
-        TemplateLoaderSession session = null;
-        try {
-            if (oldCachedResult != null) {
-                // If we're within the refresh delay, return the cached result
-                if (now - oldCachedResult.lastChecked < 
templateUpdateDelayMilliseconds) {
-                    if (debug) {
-                        LOG.debug(debugPrefix + "Cached copy not yet stale; 
using cached.");
-                    }
-                    Object t = oldCachedResult.templateOrException;
-                    // t can be null, indicating a cached negative lookup
-                    if (t instanceof Template || t == null) {
-                        return (Template) t;
-                    } else if (t instanceof RuntimeException) {
-                        rethrowCachedException((RuntimeException) t);
-                    } else if (t instanceof IOException) {
-                        rethrownCachedException = true;
-                        rethrowCachedException((IOException) t);
-                    }
-                    throw new BugException("Unhandled class for t: " + 
t.getClass().getName());
-                }
-                // The freshness of the cache result must be checked.
-                
-                // Clone, as the instance in the cache store must not be 
modified to ensure proper concurrent behavior.
-                newCachedResult = oldCachedResult.clone();
-                newCachedResult.lastChecked = now;
-
-                session = templateLoader.createSession();
-                if (debug && session != null) {
-                    LOG.debug(debugPrefix + "Session created.");
-                }
-                
-                // Find the template source, load it if it doesn't correspond 
to the cached result.
-                newLookupResult = lookupAndLoadTemplateIfChanged(
-                        name, locale, customLookupCondition, 
oldCachedResult.source, oldCachedResult.version, session);
-
-                // Template source was removed 
(TemplateLoader2ResultStatus.NOT_FOUND, or no TemplateLoader2Result)
-                if (!newLookupResult.isPositive()) { 
-                    if (debug) {
-                        LOG.debug(debugPrefix + "No source found.");
-                    } 
-                    setToNegativeAndPutIntoCache(cacheKey, newCachedResult, 
null);
-                    return null;
-                }
-
-                final TemplateLoadingResult newTemplateLoaderResult = 
newLookupResult.getTemplateLoaderResult();
-                if (newTemplateLoaderResult.getStatus() == 
TemplateLoadingResultStatus.NOT_MODIFIED) {
-                    // Return the cached version.
-                    if (debug) {
-                        LOG.debug(debugPrefix + ": Using cached template "
-                                + "(source: " + 
newTemplateLoaderResult.getSource() + ")"
-                                + " as it hasn't been changed on the backing 
store.");
-                    }
-                    putIntoCache(cacheKey, newCachedResult);
-                    return (Template) newCachedResult.templateOrException;
-                } else {
-                    if (newTemplateLoaderResult.getStatus() != 
TemplateLoadingResultStatus.OPENED) {
-                        // TemplateLoader2ResultStatus.NOT_FOUND was already 
handler earlier
-                        throw new BugException("Unxpected status: " + 
newTemplateLoaderResult.getStatus());
-                    }
-                    if (debug) {
-                        StringBuilder debugMsg = new StringBuilder();
-                        debugMsg.append(debugPrefix)
-                                .append("Reloading template instead of using 
the cached result because ");
-                        if (newCachedResult.templateOrException instanceof 
Throwable) {
-                            debugMsg.append("it's a cached error (retrying).");
-                        } else {
-                            Object newSource = 
newTemplateLoaderResult.getSource();
-                            if (!nullSafeEquals(newSource, 
oldCachedResult.source)) {
-                                debugMsg.append("the source has been changed: 
")
-                                        
.append("cached.source=").append(StringUtil.jQuoteNoXSS(oldCachedResult.source))
-                                        .append(", 
current.source=").append(StringUtil.jQuoteNoXSS(newSource));
-                            } else {
-                                Serializable newVersion = 
newTemplateLoaderResult.getVersion();
-                                if (!nullSafeEquals(oldCachedResult.version, 
newVersion)) {
-                                    debugMsg.append("the version has been 
changed: ")
-                                            
.append("cached.version=").append(oldCachedResult.version) 
-                                            .append(", 
current.version=").append(newVersion);
-                                } else {
-                                    debugMsg.append("??? (unknown reason)");
-                                }
-                            }
-                        }
-                        LOG.debug(debugMsg.toString());
-                    }
-                }
-            } else { // if there was no cached result
-                if (debug) {
-                    LOG.debug(debugPrefix + "No cached result was found; will 
try to load template.");
-                }
-                
-                newCachedResult = new CachedResult();
-                newCachedResult.lastChecked = now;
-            
-                session = templateLoader.createSession();
-                if (debug && session != null) {
-                    LOG.debug(debugPrefix + "Session created.");
-                } 
-                
-                newLookupResult = lookupAndLoadTemplateIfChanged(
-                        name, locale, customLookupCondition, null, null, 
session);
-                
-                if (!newLookupResult.isPositive()) {
-                    setToNegativeAndPutIntoCache(cacheKey, newCachedResult, 
null);
-                    return null;
-                }
-            }
-            // We have newCachedResult and newLookupResult initialized at this 
point.
-
-            TemplateLoadingResult templateLoaderResult = 
newLookupResult.getTemplateLoaderResult();
-            newCachedResult.source = templateLoaderResult.getSource();
-            
-            // If we get here, then we need to (re)load the template
-            if (debug) {
-                LOG.debug(debugPrefix + "Reading template content (source: "
-                        + StringUtil.jQuoteNoXSS(newCachedResult.source) + 
")");
-            }
-            
-            Template template = loadTemplate(
-                    templateLoaderResult,
-                    name, newLookupResult.getTemplateSourceName(), locale, 
customLookupCondition,
-                    encoding, parseAsFTL);
-            if (session != null) {
-                session.close();
-                if (debug) {
-                    LOG.debug(debugPrefix + "Session closed.");
-                } 
-            }
-            newCachedResult.templateOrException = template;
-            newCachedResult.version = templateLoaderResult.getVersion();
-            putIntoCache(cacheKey, newCachedResult);
-            return template;
-        } catch (RuntimeException e) {
-            if (newCachedResult != null) {
-                setToNegativeAndPutIntoCache(cacheKey, newCachedResult, e);
-            }
-            suppressFinallyException = true;
-            throw e;
-        } catch (IOException e) {
-            // Rethrown cached exceptions are wrapped into IOException-s, so 
we only need this condition here.
-            if (!rethrownCachedException) {
-                setToNegativeAndPutIntoCache(cacheKey, newCachedResult, e);
-            }
-            suppressFinallyException = true;
-            throw e;
-        } finally {
-            try {
-                // Close streams first:
-                
-                if (newLookupResult != null && newLookupResult.isPositive()) {
-                    TemplateLoadingResult templateLoaderResult = 
newLookupResult.getTemplateLoaderResult();
-                    Reader reader = templateLoaderResult.getReader();
-                    if (reader != null) {
-                        try {
-                            reader.close();
-                        } catch (IOException e) { // [FM3] Exception e
-                            if (suppressFinallyException) {
-                                if (LOG.isWarnEnabled()) { 
-                                    LOG.warn("Failed to close template content 
Reader for: " + name, e);
-                                }
-                            } else {
-                                suppressFinallyException = true;
-                                throw e;
-                            }
-                        }
-                    } else if (templateLoaderResult.getInputStream() != null) {
-                        try {
-                            templateLoaderResult.getInputStream().close();
-                        } catch (IOException e) { // [FM3] Exception e
-                            if (suppressFinallyException) {
-                                if (LOG.isWarnEnabled()) { 
-                                    LOG.warn("Failed to close template content 
InputStream for: " + name, e);
-                                }
-                            } else {
-                                suppressFinallyException = true;
-                                throw e;
-                            }
-                        }
-                    }
-                }
-            } finally {
-                // Then close streams:
-                
-                if (session != null && !session.isClosed()) {
-                    try {
-                        session.close();
-                        if (debug) {
-                            LOG.debug(debugPrefix + "Session closed.");
-                        } 
-                    } catch (IOException e) { // [FM3] Exception e
-                        if (suppressFinallyException) {
-                            if (LOG.isWarnEnabled()) { 
-                                LOG.warn("Failed to close template loader 
session for" + name, e);
-                            }
-                        } else {
-                            suppressFinallyException = true;
-                            throw e;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    
-    
-    private static final Method INIT_CAUSE = getInitCauseMethod();
-    
-    private static final Method getInitCauseMethod() {
-        try {
-            return Throwable.class.getMethod("initCause", new Class[] { 
Throwable.class });
-        } catch (NoSuchMethodException e) {
-            return null;
-        }
-    }
-    
-    /**
-     * Creates an {@link IOException} that has a cause exception.
-     */
-    // [Java 6] Remove
-    private IOException newIOException(String message, Throwable cause) {
-        if (cause == null) {
-            return new IOException(message);
-        }
-        
-        IOException ioe;
-        if (INIT_CAUSE != null) {
-            ioe = new IOException(message);
-            try {
-                INIT_CAUSE.invoke(ioe, cause);
-            } catch (RuntimeException ex) {
-                throw ex;
-            } catch (Exception ex) {
-                throw new UndeclaredThrowableException(ex);
-            }
-        } else {
-            ioe = new IOException(message + "\nCaused by: " + 
cause.getClass().getName() + 
-            ": " + cause.getMessage());
-        }
-        return ioe;
-    }
-    
-    private void rethrowCachedException(Throwable e) throws IOException {
-        throw newIOException("There was an error loading the " +
-                "template on an earlier attempt; see cause exception.", e);
-    }
-
-    private void setToNegativeAndPutIntoCache(CachedResultKey cacheKey, 
CachedResult cachedResult, Exception e) {
-        cachedResult.templateOrException = e;
-        cachedResult.source = null;
-        cachedResult.version = null;
-        putIntoCache(cacheKey, cachedResult);
-    }
-
-    private void putIntoCache(CachedResultKey tk, CachedResult cachedTemplate) 
{
-        if (isCacheStorageConcurrent) {
-            cacheStorage.put(tk, cachedTemplate);
-        } else {
-            synchronized (cacheStorage) {
-                cacheStorage.put(tk, cachedTemplate);
-            }
-        }
-    }
-
-    @SuppressWarnings("deprecation")
-    private Template loadTemplate(
-            TemplateLoadingResult templateLoaderResult,
-            final String name, final String sourceName, Locale locale, final 
Object customLookupCondition,
-            String initialEncoding, final boolean parseAsFTL) throws 
IOException {
-        TemplateConfiguration tc;
-        {
-            TemplateConfiguration cfgTC;
-            try {
-                cfgTC = templateConfigurations != null
-                        ? templateConfigurations.get(sourceName, 
templateLoaderResult.getSource()) : null;
-            } catch (TemplateConfigurationFactoryException e) {
-                throw newIOException("Error while getting 
TemplateConfiguration; see cause exception.", e);
-            }
-            TemplateConfiguration resultTC = 
templateLoaderResult.getTemplateConfiguration();
-            if (resultTC != null) {
-                TemplateConfiguration mergedTC = new TemplateConfiguration();
-                if (cfgTC != null) {
-                    mergedTC.merge(cfgTC);
-                }
-                if (resultTC != null) {
-                    mergedTC.merge(resultTC);
-                }
-                mergedTC.setParentConfiguration(config);
-                
-                tc = mergedTC;
-            } else {
-                tc = cfgTC;
-            }
-        }
-        
-        if (tc != null) {
-            // TC.{encoding,locale} is stronger than the cfg.getTemplate 
arguments by design.
-            if (tc.isEncodingSet()) {
-                initialEncoding = tc.getEncoding();
-            }
-            if (tc.isLocaleSet()) {
-                locale = tc.getLocale();
-            }
-        }
-        
-        Template template;
-        {
-            Reader reader = templateLoaderResult.getReader();
-            InputStream inputStream = templateLoaderResult.getInputStream();
-            TemplateSpecifiedEncodingHandler templateSpecifiedEncodingHandler;
-            if (reader != null) {
-                if (inputStream != null) {
-                    throw new IllegalStateException("For a(n) " + 
templateLoaderResult.getClass().getName()
-                            + ", both getReader() and getInputStream() has 
returned non-null.");
-                }
-                initialEncoding = null;  // No charset decoding has happened
-                templateSpecifiedEncodingHandler = 
TemplateSpecifiedEncodingHandler.DEFAULT; 
-            } else if (inputStream != null) {
-                if (parseAsFTL) {
-                    // We need mark support, to restart if the charset 
suggested by <#ftl encoding=...> differs
-                    // from that we use initially.
-                    if (!inputStream.markSupported()) {
-                        inputStream = new BufferedInputStream(inputStream);
-                    }
-                    inputStream.mark(Integer.MAX_VALUE); // Mark is released 
after the 1st FTL tag
-                    templateSpecifiedEncodingHandler = new 
MarkReleaserTemplateSpecifiedEncodingHandler(inputStream);
-                } else {
-                    templateSpecifiedEncodingHandler = null; 
-                }
-                // Regarding buffering worries: On the Reader side we should 
only read in chunks (like through a
-                // BufferedReader), so there shouldn't be a problem if the 
InputStream is not buffered. (Also, at least
-                // on Oracle JDK and OpenJDK 7 the InputStreamReader itself 
has an internal ~8K buffer.)
-                reader = new InputStreamReader(inputStream, initialEncoding);
-            } else {
-                throw new IllegalStateException("For a(n) " + 
templateLoaderResult.getClass().getName()
-                        + ", both getReader() and getInputStream() has 
returned null.");
-            }
-            
-            try {
-                if (parseAsFTL) {
-                    try {
-                        template = new Template(name, sourceName, reader, 
config, tc,
-                                initialEncoding, 
templateSpecifiedEncodingHandler);
-                    } catch (Template.WrongEncodingException wee) {
-                        final String templateSpecifiedEncoding = 
wee.getTemplateSpecifiedEncoding();
-                        
-                        if (inputStream != null) {
-                            // We restart InputStream to re-decode it with the 
new charset.
-                            inputStream.reset();
-                            
-                            // Don't close `reader`; it's an InputStreamReader 
that would close the wrapped InputStream.
-                            reader = new InputStreamReader(inputStream, 
templateSpecifiedEncoding);
-                        } else {
-                            // Should be impossible to get here
-                            throw new BugException();
-                        }
-                        
-                        template = new Template(name, sourceName, reader, 
config, tc,
-                                templateSpecifiedEncoding, 
templateSpecifiedEncodingHandler);
-                    }
-                } else {
-                    // Read the contents into a StringWriter, then construct a 
single-text-block template from it.
-                    final StringBuilder sb = new StringBuilder();
-                    final char[] buf = new char[4096];
-                    int charsRead;
-                    while ((charsRead = reader.read(buf)) > 0) {
-                        sb.append(buf, 0, charsRead);
-                    }
-                    template = Template.getPlainTextTemplate(name, sourceName, 
sb.toString(), config);
-                    template.setEncoding(initialEncoding);
-                }
-            } finally {
-                reader.close();
-            }
-        }
-
-        if (tc != null) {
-            tc.apply(template);
-        }
-        
-        template.setLocale(locale);
-        template.setCustomLookupCondition(customLookupCondition);
-        return template;
-    }
-
-    /**
-     * 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;
-        }
-    }
-
-    /**
-     * Sets the delay in milliseconds between checking for newer versions of a
-     * template sources.
-     * @param templateUpdateDelayMilliseconds the new value of the delay
-     */
-    public void setTemplateUpdateDelayMilliseconds(long 
templateUpdateDelayMilliseconds) {
-        // synchronized was moved here so that we don't advertise that it's 
thread-safe, as it's not.
-        synchronized (this) {
-            this.templateUpdateDelayMilliseconds = 
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;
-        }
-    }
-
-    /**
-     * Setis if localized template lookup is enabled or not.
-     */
-    public void setLocalizedLookup(boolean localizedLookup) {
-        // synchronized was moved here so that we don't advertise that it's 
thread-safe, as it's not.
-        synchronized (this) {
-            if (this.localizedLookup != localizedLookup) {
-                this.localizedLookup = localizedLookup;
-                clearTemplateCache();
-            }
-        }
-    }
-
-    /**
-     * Removes all entries from the cache, forcing reloading of templates on 
subsequent
-     * {@link #getTemplate(String, Locale, Object, String, boolean)} calls.
-     * 
-     * @param resetTemplateLoader
-     *            Whether to call {@link TemplateLoader#resetState()}. on the 
template loader.
-     */
-    public void clearTemplateCache(boolean resetTemplateLoader) {
-        synchronized (cacheStorage) {
-            cacheStorage.clear();
-            if (templateLoader != null && resetTemplateLoader) {
-                templateLoader.resetState();
-            }
-        }
-    }
-    
-    /**
-     * Same as {@link #clearTemplateCache(boolean)} with {@code true} {@code 
resetTemplateLoader} argument.
-     */
-    @Override
-    public void clearTemplateCache() {
-        synchronized (cacheStorage) {
-            cacheStorage.clear();
-            if (templateLoader != null) {
-                templateLoader.resetState();
-            }
-        }
-    }
-
-    /**
-     * Same as {@link #removeTemplateFromCache(String, Locale, Object, String, 
boolean)} with {@code null}
-     * {@code customLookupCondition}.
-     */
-    @Override
-    public void removeTemplateFromCache(
-            String name, Locale locale, String encoding, boolean parse) throws 
IOException {
-        removeTemplateFromCache(name, locale, null, encoding, parse);
-    }
-    
-    /**
-     * 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
-     * {@link #setTemplateUpdateDelayMilliseconds(long)} alone does.
-     * 
-     * For the meaning of the parameters, see
-     * {@link Configuration#getTemplate(String, Locale, Object, String, 
boolean, boolean)}
-     */
-    public void removeTemplateFromCache(
-            String name, Locale locale, Object customLookupCondition, String 
encoding, boolean parse)
-    throws IOException {
-        if (name == null) {
-            throw new IllegalArgumentException("Argument \"name\" can't be 
null");
-        }
-        if (locale == null) {
-            throw new IllegalArgumentException("Argument \"locale\" can't be 
null");
-        }
-        if (encoding == null) {
-            throw new IllegalArgumentException("Argument \"encoding\" can't be 
null");
-        }
-        name = templateNameFormat.normalizeRootBasedName(name);
-        if (name != null && templateLoader != null) {
-            boolean debug = LOG.isDebugEnabled();
-            String debugPrefix = debug
-                    ? getDebugPrefix("removeTemplate", name, locale, 
customLookupCondition, encoding, parse)
-                    : null;
-            CachedResultKey tk = new CachedResultKey(name, locale, 
customLookupCondition, encoding, parse);
-            
-            if (isCacheStorageConcurrent) {
-                cacheStorage.remove(tk);
-            } else {
-                synchronized (cacheStorage) {
-                    cacheStorage.remove(tk);
-                }
-            }
-            if (debug) {
-                LOG.debug(debugPrefix + "Template was removed from the cache, 
if it was there");
-            }
-        }
-    }
-
-    private String getDebugPrefix(String operation, String name, Locale 
locale, Object customLookupCondition, String encoding,
-            boolean parse) {
-        return operation + " " + StringUtil.jQuoteNoXSS(name) + "("
-                + StringUtil.jQuoteNoXSS(locale)
-                + (customLookupCondition != null ? ", cond=" + 
StringUtil.jQuoteNoXSS(customLookupCondition) : "")
-                + ", " + encoding
-                + (parse ? ", parsed)" : ", unparsed]")
-                + ": ";
-    }    
-
-    /**
-     * Looks up according the {@link TemplateLookupStrategy} and then starts 
reading the template, if it was changed
-     * compared to the cached result, or if there was no cached result yet.
-     */
-    private TemplateLookupResult lookupAndLoadTemplateIfChanged(
-            String name, Locale locale, Object customLookupCondition,
-            TemplateLoadingSource cachedResultSource, Serializable 
cachedResultVersion,
-            TemplateLoaderSession session) throws IOException {
-        final TemplateLookupResult lookupResult = 
templateLookupStrategy.lookup(
-                new DefaultTemplateResolverTemplateLookupContext(
-                        name, locale, customLookupCondition,
-                        cachedResultSource, cachedResultVersion,
-                        session));
-        if (lookupResult == null) {
-            throw new NullPointerException("Lookup result shouldn't be null");
-        }
-        return lookupResult;
-    }
-
-    private String concatPath(List<String> pathSteps, int from, int to) {
-        StringBuilder buf = new StringBuilder((to - from) * 16);
-        for (int i = from; i < to; ++i) {
-            buf.append(pathSteps.get(i));
-            if (i < pathSteps.size() - 1) {
-                buf.append('/');
-            }
-        }
-        return buf.toString();
-    }
-    
-    // Replace with Objects.equals in Java 7
-    private static boolean nullSafeEquals(Object o1, Object o2) {
-        if (o1 == o2) return true;
-        if (o1 == null || o2 == null) return false;
-        return o1.equals(o2);
-    }
-    
-    /**
-     * Used as cache key to look up a {@link CachedResult}. 
-     */
-    @SuppressWarnings("serial")
-    private static final class CachedResultKey implements Serializable {
-        private final String name;
-        private final Locale locale;
-        private final Object customLookupCondition;
-        private final String encoding;
-        private final boolean parse;
-
-        CachedResultKey(String name, Locale locale, Object 
customLookupCondition, String encoding, boolean parse) {
-            this.name = name;
-            this.locale = locale;
-            this.customLookupCondition = customLookupCondition;
-            this.encoding = encoding;
-            this.parse = parse;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof CachedResultKey) {
-                CachedResultKey tk = (CachedResultKey) o;
-                return
-                    parse == tk.parse &&
-                    name.equals(tk.name) &&
-                    locale.equals(tk.locale) &&
-                    nullSafeEquals(customLookupCondition, 
tk.customLookupCondition) &&
-                    encoding.equals(tk.encoding);
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return
-                name.hashCode() ^
-                locale.hashCode() ^
-                encoding.hashCode() ^
-                (customLookupCondition != null ? 
customLookupCondition.hashCode() : 0) ^
-                Boolean.valueOf(!parse).hashCode();
-        }
-    }
-
-    /**
-     * Hold the a cached {@link #getTemplate(String, Locale, Object, String, 
boolean)} result and the associated
-     * information needed to check if the cached value is up to date.
-     * 
-     * <p>
-     * Note: this class is Serializable to allow custom 3rd party CacheStorage 
implementations to serialize/replicate
-     * them; FreeMarker code itself doesn't rely on its serializability.
-     * 
-     * @see CachedResultKey
-     */
-    private static final class CachedResult implements Cloneable, Serializable 
{
-        private static final long serialVersionUID = 1L;
-
-        Object templateOrException;
-        TemplateLoadingSource source;
-        Serializable version;
-        long lastChecked;
-        
-        @Override
-        public CachedResult clone() {
-            try {
-                return (CachedResult) super.clone();
-            } catch (CloneNotSupportedException e) {
-                throw new UndeclaredThrowableException(e);
-            }
-        }
-    }
-    
-    private class DefaultTemplateResolverTemplateLookupContext extends 
TemplateLookupContext {
-
-        private final TemplateLoaderSession session; 
-        
-        DefaultTemplateResolverTemplateLookupContext(String templateName, 
Locale templateLocale, Object customLookupCondition,
-                TemplateLoadingSource cachedResultSource, Serializable 
cachedResultVersion,
-                TemplateLoaderSession session) {
-            super(templateName, localizedLookup ? templateLocale : null, 
customLookupCondition,
-                    cachedResultSource, cachedResultVersion);
-            this.session = session;
-        }
-
-        @Override
-        public TemplateLookupResult lookupWithAcquisitionStrategy(String path) 
throws IOException {
-            // Only one of the possible ways of making a name non-normalized, 
but is the easiest mistake to do:
-            if (path.startsWith("/")) {
-                throw new IllegalArgumentException("Non-normalized name, 
starts with \"/\": " + path);
-            }
-            
-            int asterisk = path.indexOf(ASTERISK);
-            // Shortcut in case there is no acquisition
-            if (asterisk == -1) {
-                return TemplateLookupResult.from(
-                        path,
-                        templateLoader.load(path, getCachedResultSource(), 
getCachedResultVersion(), session));
-            }
-            StringTokenizer pathTokenizer = new StringTokenizer(path, "/");
-            int lastAsterisk = -1;
-            List<String> pathSteps = new ArrayList<String>();
-            while (pathTokenizer.hasMoreTokens()) {
-                String pathStep = pathTokenizer.nextToken();
-                if (pathStep.equals(ASTERISKSTR)) {
-                    if (lastAsterisk != -1) {
-                        pathSteps.remove(lastAsterisk);
-                    }
-                    lastAsterisk = pathSteps.size();
-                }
-                pathSteps.add(pathStep);
-            }
-            if (lastAsterisk == -1) {  // if there was no real "*" step after 
all
-                return TemplateLookupResult.from(
-                        path,
-                        templateLoader.load(path, getCachedResultSource(), 
getCachedResultVersion(), session));
-            }
-            String basePath = concatPath(pathSteps, 0, lastAsterisk);
-            String postAsteriskPath = concatPath(pathSteps, lastAsterisk + 1, 
pathSteps.size());
-            StringBuilder buf = new 
StringBuilder(path.length()).append(basePath);
-            int basePathLen = basePath.length();
-            while (true) {
-                String fullPath = buf.append(postAsteriskPath).toString();
-                TemplateLoadingResult templateLoaderResult = 
templateLoader.load(
-                        fullPath, getCachedResultSource(), 
getCachedResultVersion(), session);
-                if (templateLoaderResult.getStatus() == 
TemplateLoadingResultStatus.OPENED) {
-                    return TemplateLookupResult.from(fullPath, 
templateLoaderResult);
-                }
-                if (basePathLen == 0) {
-                    return TemplateLookupResult.createNegativeResult();
-                }
-                basePathLen = basePath.lastIndexOf(SLASH, basePathLen - 2) + 1;
-                buf.setLength(basePathLen);
-            }
-        }
-
-        @Override
-        public TemplateLookupResult 
lookupWithLocalizedThenAcquisitionStrategy(final String templateName,
-                final Locale templateLocale) throws IOException {
-            
-                if (templateLocale == null) {
-                    return lookupWithAcquisitionStrategy(templateName);
-                }
-                
-                int lastDot = templateName.lastIndexOf('.');
-                String prefix = lastDot == -1 ? templateName : 
templateName.substring(0, lastDot);
-                String suffix = lastDot == -1 ? "" : 
templateName.substring(lastDot);
-                String localeName = LOCALE_PART_SEPARATOR + 
templateLocale.toString();
-                StringBuilder buf = new StringBuilder(templateName.length() + 
localeName.length());
-                buf.append(prefix);
-                tryLocaleNameVariations: while (true) {
-                    buf.setLength(prefix.length());
-                    String path = 
buf.append(localeName).append(suffix).toString();
-                    TemplateLookupResult lookupResult = 
lookupWithAcquisitionStrategy(path);
-                    if (lookupResult.isPositive()) {
-                        return lookupResult;
-                    }
-                    
-                    int lastUnderscore = localeName.lastIndexOf('_');
-                    if (lastUnderscore == -1) {
-                        break tryLocaleNameVariations;
-                    }
-                    localeName = localeName.substring(0, lastUnderscore);
-                }
-                return createNegativeLookupResult();
-        }
-        
-    }
-
-    @Override
-    public String toRootBasedName(String baseName, String targetName) throws 
MalformedTemplateNameException {
-        return templateNameFormat.toRootBasedName(baseName, targetName);
-    }
-
-    @Override
-    public String normalizeRootBasedName(String name) throws 
MalformedTemplateNameException {
-        return templateNameFormat.normalizeRootBasedName(name);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/FileTemplateLoader.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/templateresolver/FileTemplateLoader.java
 
b/src/main/java/org/apache/freemarker/core/templateresolver/FileTemplateLoader.java
deleted file mode 100644
index 2dacb2d..0000000
--- 
a/src/main/java/org/apache/freemarker/core/templateresolver/FileTemplateLoader.java
+++ /dev/null
@@ -1,380 +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;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.Objects;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core._CoreLogs;
-import org.apache.freemarker.core.util.SecurityUtilities;
-import org.apache.freemarker.core.util.StringUtil;
-import org.slf4j.Logger;
-
-/**
- * A {@link TemplateLoader} that uses files inside a specified directory as 
the source of templates. By default it does
- * security checks on the <em>canonical</em> path that will prevent it serving 
templates outside that specified
- * directory. If you want symbolic links that point outside the template 
directory to work, you need to disable this
- * feature by using {@link #FileTemplateLoader(File, boolean)} with {@code 
true} second argument, but before that,
- * check the security implications there!
- */
-public class FileTemplateLoader implements TemplateLoader {
-    
-    /**
-     * By setting this Java system property to {@code true}, you can change 
the default of
-     * {@code #getEmulateCaseSensitiveFileSystem()}.
-     */
-    public static String 
SYSTEM_PROPERTY_NAME_EMULATE_CASE_SENSITIVE_FILE_SYSTEM
-            = "org.freemarker.emulateCaseSensitiveFileSystem";
-    private static final boolean EMULATE_CASE_SENSITIVE_FILE_SYSTEM_DEFAULT;
-    static {
-        final String s = 
SecurityUtilities.getSystemProperty(SYSTEM_PROPERTY_NAME_EMULATE_CASE_SENSITIVE_FILE_SYSTEM,
-                "false");
-        boolean emuCaseSensFS;
-        try {
-            emuCaseSensFS = StringUtil.getYesNo(s);
-        } catch (Exception e) {
-            emuCaseSensFS = false;
-        }
-        EMULATE_CASE_SENSITIVE_FILE_SYSTEM_DEFAULT = emuCaseSensFS;
-    }
-
-    private static final int CASE_CHECH_CACHE_HARD_SIZE = 50;
-    private static final int CASE_CHECK_CACHE__SOFT_SIZE = 1000;
-    private static final boolean SEP_IS_SLASH = File.separatorChar == '/';
-    
-    private static final Logger LOG = _CoreLogs.TEMPLATE_RESOLVER;
-    
-    public final File baseDir;
-    private final String canonicalBasePath;
-    private boolean emulateCaseSensitiveFileSystem;
-    private MruCacheStorage correctCasePaths;
-
-    /**
-     * Creates a new file template loader that will use the specified directory
-     * as the base directory for loading templates. It will not allow access to
-     * template files that are accessible through symlinks that point outside 
-     * the base directory.
-     * @param baseDir the base directory for loading templates
-     */
-    public FileTemplateLoader(final File baseDir)
-    throws IOException {
-        this(baseDir, false);
-    }
-
-    /**
-     * Creates a new file template loader that will use the specified 
directory as the base directory for loading
-     * templates. See the parameters for allowing symlinks that point outside 
the base directory.
-     * 
-     * @param baseDir
-     *            the base directory for loading templates
-     * 
-     * @param disableCanonicalPathCheck
-     *            If {@code true}, it will not check if the file to be loaded 
is inside the {@code baseDir} or not,
-     *            according the <em>canonical</em> paths of the {@code 
baseDir} and the file to load. Note that
-     *            {@link Configuration#getTemplate(String)} and (its 
overloads) already prevents backing out from the
-     *            template directory with paths like {@code 
/../../../etc/password}, however, that can be circumvented
-     *            with symbolic links or other file system features. If you 
really want to use symbolic links that point
-     *            outside the {@code baseDir}, set this parameter to {@code 
true}, but then be very careful with
-     *            template paths that are supplied by the visitor or an 
external system.
-     */
-    public FileTemplateLoader(final File baseDir, final boolean 
disableCanonicalPathCheck)
-    throws IOException {
-        try {
-            Object[] retval = AccessController.doPrivileged(new 
PrivilegedExceptionAction<Object[]>() {
-                @Override
-                public Object[] run() throws IOException {
-                    if (!baseDir.exists()) {
-                        throw new FileNotFoundException(baseDir + " does not 
exist.");
-                    }
-                    if (!baseDir.isDirectory()) {
-                        throw new IOException(baseDir + " is not a 
directory.");
-                    }
-                    Object[] retval = new Object[2];
-                    if (disableCanonicalPathCheck) {
-                        retval[0] = baseDir;
-                        retval[1] = null;
-                    } else {
-                        retval[0] = baseDir.getCanonicalFile();
-                        String basePath = ((File) retval[0]).getPath();
-                        // Most canonical paths don't end with File.separator,
-                        // but some does. Like, "C:\" VS "C:\templates".
-                        if (!basePath.endsWith(File.separator)) {
-                            basePath += File.separatorChar;
-                        }
-                        retval[1] = basePath;
-                    }
-                    return retval;
-                }
-            });
-            this.baseDir = (File) retval[0];
-            this.canonicalBasePath = (String) retval[1];
-            
-            
setEmulateCaseSensitiveFileSystem(getEmulateCaseSensitiveFileSystemDefault());
-        } catch (PrivilegedActionException e) {
-            throw (IOException) e.getException();
-        }
-    }
-    
-    private File getFile(final String name) throws IOException {
-        try {
-            return AccessController.doPrivileged(new 
PrivilegedExceptionAction<File>() {
-                @Override
-                public File run() throws IOException {
-                    File source = new File(baseDir, SEP_IS_SLASH ? name : 
-                        name.replace('/', File.separatorChar));
-                    if (!source.isFile()) {
-                        return null;
-                    }
-                    // Security check for inadvertently returning something 
-                    // outside the template directory when linking is not 
-                    // allowed.
-                    if (canonicalBasePath != null) {
-                        String normalized = source.getCanonicalPath();
-                        if (!normalized.startsWith(canonicalBasePath)) {
-                            throw new 
SecurityException(source.getAbsolutePath() 
-                                    + " resolves to " + normalized + " which " 
+ 
-                                    " doesn't start with " + 
canonicalBasePath);
-                        }
-                    }
-                    
-                    if (emulateCaseSensitiveFileSystem && 
!isNameCaseCorrect(source)) {
-                        return null;
-                    }
-                    
-                    return source;
-                }
-            });
-        } catch (PrivilegedActionException e) {
-            throw (IOException) e.getException();
-        }
-    }
-    
-    private long getLastModified(final File templateSource) {
-        return (AccessController.<Long>doPrivileged(new 
PrivilegedAction<Long>() {
-            @Override
-            public Long run() {
-                return Long.valueOf((templateSource).lastModified());
-            }
-        })).longValue();
-    }
-    
-    private InputStream getInputStream(final File templateSource)
-    throws IOException {
-        try {
-            return AccessController.doPrivileged(new 
PrivilegedExceptionAction<InputStream>() {
-                @Override
-                public InputStream run() throws IOException {
-                    return new FileInputStream(templateSource);
-                }
-            });
-        } catch (PrivilegedActionException e) {
-            throw (IOException) e.getException();
-        }
-    }
-    
-    /**
-     * Called by {@link #getFile(String)} when {@link 
#getEmulateCaseSensitiveFileSystem()} is {@code true}.
-     */
-    private boolean isNameCaseCorrect(File source) throws IOException {
-        final String sourcePath = source.getPath();
-        synchronized (correctCasePaths) {
-            if (correctCasePaths.get(sourcePath) != null) {
-                return true;
-            }
-        }
-        
-        final File parentDir = source.getParentFile();
-        if (parentDir != null) {
-            if (!baseDir.equals(parentDir) && !isNameCaseCorrect(parentDir)) {
-                return false;
-            }
-            
-            final String[] listing = parentDir.list();
-            if (listing != null) {
-                final String fileName = source.getName();
-                
-                boolean identicalNameFound = false;
-                for (int i = 0; !identicalNameFound && i < listing.length; 
i++) {
-                    if (fileName.equals(listing[i])) {
-                        identicalNameFound = true;
-                    }
-                }
-        
-                if (!identicalNameFound) {
-                    // If we find a similarly named file that only differs in 
case, then this is a file-not-found.
-                    for (int i = 0; i < listing.length; i++) {
-                        final String listingEntry = listing[i];
-                        if (fileName.equalsIgnoreCase(listingEntry)) {
-                            if (LOG.isDebugEnabled()) {
-                                LOG.debug("Emulating file-not-found because of 
letter case differences to the "
-                                        + "real file, for: {}", sourcePath);
-                            }
-                            return false;
-                        }
-                    }
-                }
-            }
-        }
-
-        synchronized (correctCasePaths) {
-            correctCasePaths.put(sourcePath, Boolean.TRUE);        
-        }
-        return true;
-    }
-    
-    /**
-     * Returns the base directory in which the templates are searched. This 
comes from the constructor argument, but
-     * it's possibly a canonicalized version of that. 
-     *  
-     * @since 2.3.21
-     */
-    public File getBaseDirectory() {
-        return baseDir;
-    }
-    
-    /**
-     * Intended for development only, checks if the template name matches the 
case (upper VS lower case letters) of the
-     * actual file name, and if it doesn't, it emulates a file-not-found even 
if the file system is case insensitive.
-     * This is useful when developing application on Windows, which will be 
later installed on Linux, OS X, etc. This
-     * check can be resource intensive, as to check the file name the 
directories involved, up to the
-     * {@link #getBaseDirectory()} directory, must be listed. Positive results 
(matching case) will be cached without
-     * expiration time.
-     * 
-     * <p>The default in {@link FileTemplateLoader} is {@code false}, but 
subclasses may change they by overriding
-     * {@link #getEmulateCaseSensitiveFileSystemDefault()}.
-     * 
-     * @since 2.3.23
-     */
-    public void setEmulateCaseSensitiveFileSystem(boolean nameCaseChecked) {
-        // Ensure that the cache exists exactly when needed:
-        if (nameCaseChecked) {
-            if (correctCasePaths == null) {
-                correctCasePaths = new 
MruCacheStorage(CASE_CHECH_CACHE_HARD_SIZE, CASE_CHECK_CACHE__SOFT_SIZE);
-            }
-        } else {
-            correctCasePaths = null;
-        }
-        
-        this.emulateCaseSensitiveFileSystem = nameCaseChecked;
-    }
-
-    /**
-     * Getter pair of {@link #setEmulateCaseSensitiveFileSystem(boolean)}.
-     * 
-     * @since 2.3.23
-     */
-    public boolean getEmulateCaseSensitiveFileSystem() {
-        return emulateCaseSensitiveFileSystem;
-    }
-
-    /**
-     * Returns the default of {@link #getEmulateCaseSensitiveFileSystem()}. In 
{@link FileTemplateLoader} it's
-     * {@code false}, unless the {@link 
#SYSTEM_PROPERTY_NAME_EMULATE_CASE_SENSITIVE_FILE_SYSTEM} system property was
-     * set to {@code true}, but this can be overridden here in custom 
subclasses. For example, if your environment
-     * defines something like developer mode, you may want to override this to 
return {@code true} on Windows.
-     * 
-     * @since 2.3.23
-     */
-    protected boolean getEmulateCaseSensitiveFileSystemDefault() {
-        return EMULATE_CASE_SENSITIVE_FILE_SYSTEM_DEFAULT;
-    }
-
-    /**
-     * Show class name and some details that are useful in template-not-found 
errors.
-     * 
-     * @since 2.3.21
-     */
-    @Override
-    public String toString() {
-        // We don't StringUtil.jQuote paths here, because on Windows there 
will be \\-s then that some may find
-        // confusing.
-        return _TemplateLoaderUtils.getClassNameForToString(this) + "("
-                + "baseDir=\"" + baseDir + "\""
-                + (canonicalBasePath != null ? ", canonicalBasePath=\"" + 
canonicalBasePath + "\"" : "")
-                + (emulateCaseSensitiveFileSystem ? ", 
emulateCaseSensitiveFileSystem=true" : "")
-                + ")";
-    }
-
-    @Override
-    public TemplateLoaderSession createSession() {
-        return null;
-    }
-
-    @Override
-    public TemplateLoadingResult load(String name, TemplateLoadingSource 
ifSourceDiffersFrom,
-            Serializable ifVersionDiffersFrom, TemplateLoaderSession session) 
throws IOException {
-        File file = getFile(name);
-        if (file == null) {
-            return TemplateLoadingResult.NOT_FOUND;
-        }
-        
-        FileTemplateLoadingSource source = new FileTemplateLoadingSource(file);
-        
-        long lmd = getLastModified(file);
-        Long version = lmd != -1 ? lmd : null;
-        
-        if (ifSourceDiffersFrom != null && ifSourceDiffersFrom.equals(source) 
-                && Objects.equals(ifVersionDiffersFrom, version)) {
-            return TemplateLoadingResult.NOT_MODIFIED;
-        }
-        
-        return new TemplateLoadingResult(source, version, 
getInputStream(file), null);
-    }
-
-    @Override
-    public void resetState() {
-        // Does nothing
-    }
-    
-    @SuppressWarnings("serial")
-    private static class FileTemplateLoadingSource implements 
TemplateLoadingSource {
-        
-        private final File file;
-
-        FileTemplateLoadingSource(File file) {
-            this.file = file;
-        }
-
-        @Override
-        public int hashCode() {
-            return file.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) return true;
-            if (obj == null) return false;
-            if (getClass() != obj.getClass()) return false;
-            return file.equals(((FileTemplateLoadingSource) obj).file);
-        }
-        
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/MalformedTemplateNameException.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/templateresolver/MalformedTemplateNameException.java
 
b/src/main/java/org/apache/freemarker/core/templateresolver/MalformedTemplateNameException.java
index 6e954bb..689cf6e 100644
--- 
a/src/main/java/org/apache/freemarker/core/templateresolver/MalformedTemplateNameException.java
+++ 
b/src/main/java/org/apache/freemarker/core/templateresolver/MalformedTemplateNameException.java
@@ -23,18 +23,21 @@ import java.io.IOException;
 
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.TemplateNotFoundException;
+import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat;
+import 
org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2;
 import org.apache.freemarker.core.util.StringUtil;
 
 /**
  * Indicates that the template name given was malformed according the {@link 
TemplateNameFormat} in use. Note that for
- * backward compatibility, {@link TemplateNameFormat#DEFAULT_2_3_0} doesn't 
throw this exception,
- * {@link TemplateNameFormat#DEFAULT_2_4_0} does. This exception extends 
{@link IOException} for backward compatibility.
+ * backward compatibility, {@link DefaultTemplateNameFormatFM2} doesn't throw 
this exception,
+ * {@link DefaultTemplateNameFormat} does. This exception extends {@link 
IOException} for backward compatibility.
  * 
  * @since 2.3.22
  * 
  * @see TemplateNotFoundException
  * @see Configuration#getTemplate(String)
  */
+@SuppressWarnings("serial")
 public class MalformedTemplateNameException extends IOException {
     
     private final String templateName;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/MruCacheStorage.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/templateresolver/MruCacheStorage.java
 
b/src/main/java/org/apache/freemarker/core/templateresolver/MruCacheStorage.java
deleted file mode 100644
index 1b34662..0000000
--- 
a/src/main/java/org/apache/freemarker/core/templateresolver/MruCacheStorage.java
+++ /dev/null
@@ -1,322 +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;
-
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.SoftReference;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A cache storage that implements a two-level Most Recently Used cache. In the
- * first level, items are strongly referenced up to the specified maximum. When
- * the maximum is exceeded, the least recently used item is moved into the  
- * second level cache, where they are softly referenced, up to another 
- * specified maximum. When the second level maximum is also exceeded, the 
least 
- * recently used item is discarded altogether. This cache storage is a 
- * generalization of both {@link StrongCacheStorage} and 
- * {@link SoftCacheStorage} - the effect of both of them can be achieved by 
- * setting one maximum to zero and the other to the largest positive integer. 
- * On the other hand, if you wish to use this storage in a strong-only mode, or
- * in a soft-only mode, you might consider using {@link StrongCacheStorage} or
- * {@link SoftCacheStorage} instead, as they can be used by 
- * {@link DefaultTemplateResolver} concurrently without any synchronization on 
a 5.0 or 
- * later JRE.
- *  
- * <p>This class is <em>NOT</em> thread-safe. If it's accessed from multiple
- * threads concurrently, proper synchronization must be provided by the 
callers.
- * Note that {@link DefaultTemplateResolver}, the natural user of this class 
provides the
- * necessary synchronizations when it uses the class.
- * Also you might consider whether you need this sort of a mixed storage at all
- * in your solution, as in most cases SoftCacheStorage can also be sufficient. 
- * SoftCacheStorage will use Java soft references, and they already use access 
- * timestamps internally to bias the garbage collector against clearing 
- * recently used references, so you can get reasonably good (and 
- * memory-sensitive) most-recently-used caching through 
- * {@link SoftCacheStorage} as well.
- *
- * @see org.apache.freemarker.core.Configuration#setCacheStorage(CacheStorage)
- */
-public class MruCacheStorage implements CacheStorageWithGetSize {
-    private final MruEntry strongHead = new MruEntry();
-    private final MruEntry softHead = new MruEntry();
-    {
-        softHead.linkAfter(strongHead);
-    }
-    private final Map map = new HashMap();
-    private final ReferenceQueue refQueue = new ReferenceQueue();
-    private final int strongSizeLimit;
-    private final int softSizeLimit;
-    private int strongSize = 0;
-    private int softSize = 0;
-    
-    /**
-     * Creates a new MRU cache storage with specified maximum cache sizes. Each
-     * cache size can vary between 0 and {@link Integer#MAX_VALUE}.
-     * @param strongSizeLimit the maximum number of strongly referenced 
templates; when exceeded, the entry used
-     *          the least recently will be moved into the soft cache.
-     * @param softSizeLimit the maximum number of softly referenced templates; 
when exceeded, the entry used
-     *          the least recently will be discarded.
-     */
-    public MruCacheStorage(int strongSizeLimit, int softSizeLimit) {
-        if (strongSizeLimit < 0) throw new 
IllegalArgumentException("strongSizeLimit < 0");
-        if (softSizeLimit < 0) throw new 
IllegalArgumentException("softSizeLimit < 0");
-        this.strongSizeLimit = strongSizeLimit;
-        this.softSizeLimit = softSizeLimit;
-    }
-    
-    public Object get(Object key) {
-        removeClearedReferences();
-        MruEntry entry = (MruEntry) map.get(key);
-        if (entry == null) {
-            return null;
-        }
-        relinkEntryAfterStrongHead(entry, null);
-        Object value = entry.getValue();
-        if (value instanceof MruReference) {
-            // This can only happen with strongSizeLimit == 0
-            return ((MruReference) value).get();
-        }
-        return value;
-    }
-
-    public void put(Object key, Object value) {
-        removeClearedReferences();
-        MruEntry entry = (MruEntry) map.get(key);
-        if (entry == null) {
-            entry = new MruEntry(key, value);
-            map.put(key, entry);
-            linkAfterStrongHead(entry);
-        } else {
-            relinkEntryAfterStrongHead(entry, value);
-        }
-        
-    }
-
-    public void remove(Object key) {
-        removeClearedReferences();
-        removeInternal(key);
-    }
-
-    private void removeInternal(Object key) {
-        MruEntry entry = (MruEntry) map.remove(key);
-        if (entry != null) {
-            unlinkEntryAndInspectIfSoft(entry);
-        }
-    }
-
-    public void clear() {
-        strongHead.makeHead();
-        softHead.linkAfter(strongHead);
-        map.clear();
-        strongSize = softSize = 0;
-        // Quick refQueue processing
-        while (refQueue.poll() != null);
-    }
-
-    private void relinkEntryAfterStrongHead(MruEntry entry, Object newValue) {
-        if (unlinkEntryAndInspectIfSoft(entry) && newValue == null) {
-            // Turn soft reference into strong reference, unless is was cleared
-            MruReference mref = (MruReference) entry.getValue();
-            Object strongValue = mref.get();
-            if (strongValue != null) {
-                entry.setValue(strongValue);
-                linkAfterStrongHead(entry);
-            } else {
-                map.remove(mref.getKey());
-            }
-        } else {
-            if (newValue != null) {
-                entry.setValue(newValue);
-            }
-            linkAfterStrongHead(entry);
-        }
-    }
-
-    private void linkAfterStrongHead(MruEntry entry) {
-        entry.linkAfter(strongHead);
-        if (strongSize == strongSizeLimit) {
-            // softHead.previous is LRU strong entry
-            MruEntry lruStrong = softHead.getPrevious();
-            // Attila: This is equaivalent to strongSizeLimit != 0
-            // DD: But entry.linkAfter(strongHead) was just executed above, so
-            //     lruStrong != strongHead is true even if strongSizeLimit == 
0.
-            if (lruStrong != strongHead) {
-                lruStrong.unlink();
-                if (softSizeLimit > 0) {
-                    lruStrong.linkAfter(softHead);
-                    lruStrong.setValue(new MruReference(lruStrong, refQueue));
-                    if (softSize == softSizeLimit) {
-                        // List is circular, so strongHead.previous is LRU 
soft entry
-                        MruEntry lruSoft = strongHead.getPrevious();
-                        lruSoft.unlink();
-                        map.remove(lruSoft.getKey());
-                    } else {
-                        ++softSize;
-                    }
-                } else {
-                    map.remove(lruStrong.getKey());
-                }
-            }
-        } else {
-            ++strongSize;
-        }
-    }
-
-    private boolean unlinkEntryAndInspectIfSoft(MruEntry entry) {
-        entry.unlink();
-        if (entry.getValue() instanceof MruReference) {
-            --softSize;
-            return true;
-        } else {
-            --strongSize;
-            return false;
-        }
-    }
-    
-    private void removeClearedReferences() {
-        for (; ; ) {
-            MruReference ref = (MruReference) refQueue.poll();
-            if (ref == null) {
-                break;
-            }
-            removeInternal(ref.getKey());
-        }
-    }
-    
-    /**
-     * Returns the configured upper limit of the number of strong cache 
entries.
-     *  
-     * @since 2.3.21
-     */
-    public int getStrongSizeLimit() {
-        return strongSizeLimit;
-    }
-
-    /**
-     * Returns the configured upper limit of the number of soft cache entries.
-     * 
-     * @since 2.3.21
-     */
-    public int getSoftSizeLimit() {
-        return softSizeLimit;
-    }
-
-    /**
-     * Returns the <em>current</em> number of strong cache entries.
-     *  
-     * @see #getStrongSizeLimit()
-     * @since 2.3.21
-     */
-    public int getStrongSize() {
-        return strongSize;
-    }
-
-    /**
-     * Returns a close approximation of the <em>current</em> number of soft 
cache entries.
-     * 
-     * @see #getSoftSizeLimit()
-     * @since 2.3.21
-     */
-    public int getSoftSize() {
-        removeClearedReferences();
-        return softSize;
-    }
-    
-    /**
-     * Returns a close approximation of the current number of cache entries.
-     * 
-     * @see #getStrongSize()
-     * @see #getSoftSize()
-     * @since 2.3.21
-     */
-    public int getSize() {
-        return getSoftSize() + getStrongSize();
-    }
-
-    private static final class MruEntry {
-        private MruEntry prev;
-        private MruEntry next;
-        private final Object key;
-        private Object value;
-        
-        /**
-         * Used solely to construct the head element
-         */
-        MruEntry() {
-            makeHead();
-            key = value = null;
-        }
-        
-        MruEntry(Object key, Object value) {
-            this.key = key;
-            this.value = value;
-        }
-        
-        Object getKey() {
-            return key;
-        }
-        
-        Object getValue() {
-            return value;
-        }
-        
-        void setValue(Object value) {
-            this.value = value;
-        }
-
-        MruEntry getPrevious() {
-            return prev;
-        }
-        
-        void linkAfter(MruEntry entry) {
-            next = entry.next;
-            entry.next = this;
-            prev = entry;
-            next.prev = this;
-        }
-        
-        void unlink() {
-            next.prev = prev;
-            prev.next = next;
-            prev = null;
-            next = null;
-        }
-        
-        void makeHead() {
-            prev = next = this;
-        }
-    }
-    
-    private static class MruReference extends SoftReference {
-        private final Object key;
-        
-        MruReference(MruEntry entry, ReferenceQueue queue) {
-            super(entry.getValue(), queue);
-            this.key = entry.getKey();
-        }
-        
-        Object getKey() {
-            return key;
-        }
-    }
-    
-    
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/MultiTemplateLoader.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/templateresolver/MultiTemplateLoader.java
 
b/src/main/java/org/apache/freemarker/core/templateresolver/MultiTemplateLoader.java
deleted file mode 100644
index 0f4a7c1..0000000
--- 
a/src/main/java/org/apache/freemarker/core/templateresolver/MultiTemplateLoader.java
+++ /dev/null
@@ -1,167 +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;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.freemarker.core.util.NullArgumentException;
-
-/**
- * A {@link TemplateLoader} that uses a set of other loaders to load the 
templates. On every request, loaders are
- * queried in the order of their appearance in the array of loaders provided 
to the constructor. Except, when the
- * {@linkplain #setSticky(boolean)} sticky} setting is set to {@code true} 
(default is false {@code false}), if
- * a request for some template name was already satisfied in the past by one 
of the loaders, that loader is queried
- * first (stickiness).
- * 
- * <p>This class is thread-safe.
- */
-// TODO JUnit test
-public class MultiTemplateLoader implements TemplateLoader {
-
-    private final TemplateLoader[] templateLoaders;
-    private final Map<String, TemplateLoader> lastTemplateLoaderForName = new 
ConcurrentHashMap<String, TemplateLoader>();
-    
-    private boolean sticky = false;
-
-    /**
-     * Creates a new instance that will use the specified template loaders.
-     * 
-     * @param templateLoaders
-     *            the template loaders that are used to load templates, in the 
order as they will be searched
-     *            (except where {@linkplain #setSticky(boolean) stickiness} 
says otherwise).
-     */
-    public MultiTemplateLoader(TemplateLoader... templateLoaders) {
-        NullArgumentException.check("templateLoaders", templateLoaders);
-        this.templateLoaders = templateLoaders.clone();
-    }
-
-    /**
-     * Clears the sickiness memory, also resets the state of all enclosed 
{@link TemplateLoader}-s.
-     */
-    @Override
-    public void resetState() {
-        lastTemplateLoaderForName.clear();
-        for (TemplateLoader templateLoader : templateLoaders) {
-            templateLoader.resetState();
-        }
-    }
-
-    /**
-     * Show class name and some details that are useful in template-not-found 
errors.
-     * 
-     * @since 2.3.21
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("MultiTemplateLoader(");
-        for (int i = 0; i < templateLoaders.length; i++) {
-            if (i != 0) {
-                sb.append(", ");
-            }
-            sb.append("loader").append(i + 1).append(" = 
").append(templateLoaders[i]);
-        }
-        sb.append(")");
-        return sb.toString();
-    }
-
-    /**
-     * Returns the number of {@link TemplateLoader}-s directly inside this 
{@link TemplateLoader}.
-     * 
-     * @since 2.3.23
-     */
-    public int getTemplateLoaderCount() {
-        return templateLoaders.length;
-    }
-
-    /**
-     * Returns the {@link TemplateLoader} at the given index.
-     * 
-     * @param index
-     *            Must be below {@link #getTemplateLoaderCount()}.
-     */
-    public TemplateLoader getTemplateLoader(int index) {
-        return templateLoaders[index];
-    }
-
-    /**
-     * Getter pair of {@link #setSticky(boolean)}.
-     */
-    public boolean isSticky() {
-        return sticky;
-    }
-
-    /**
-     * Sets if for a name that was already loaded earlier the same {@link 
TemplateLoader} will be tried first, or
-     * we always try the {@link TemplateLoader}-s strictly in the order as it 
was specified in the constructor.
-     * The default is {@code false}.
-     */
-    public void setSticky(boolean sticky) {
-        this.sticky = sticky;
-    }
-
-    @Override
-    public TemplateLoaderSession createSession() {
-        return null;
-    }
-
-    @Override
-    public TemplateLoadingResult load(String name, TemplateLoadingSource 
ifSourceDiffersFrom,
-            Serializable ifVersionDiffersFrom, TemplateLoaderSession session) 
throws IOException {
-        TemplateLoader lastLoader = null;
-        if (sticky) {
-            // Use soft affinity - give the loader that last found this
-            // resource a chance to find it again first.
-            lastLoader = lastTemplateLoaderForName.get(name);
-            if (lastLoader != null) {
-                TemplateLoadingResult result = lastLoader.load(name, 
ifSourceDiffersFrom, ifVersionDiffersFrom, session);
-                if (result.getStatus() != 
TemplateLoadingResultStatus.NOT_FOUND) {
-                    return result;
-                }
-            }
-        }
-
-        // If there is no affine loader, or it could not find the resource
-        // again, try all loaders in order of appearance. If any manages
-        // to find the resource, then associate it as the new affine loader
-        // for this resource.
-        for (TemplateLoader templateLoader : templateLoaders) {
-            if (lastLoader != templateLoader) {
-                TemplateLoadingResult result = templateLoader.load(
-                        name, ifSourceDiffersFrom, ifVersionDiffersFrom, 
session);
-                if (result.getStatus() != 
TemplateLoadingResultStatus.NOT_FOUND) {
-                    if (sticky) {
-                        lastTemplateLoaderForName.put(name, templateLoader);
-                    }
-                    return result;
-                }
-            }
-        }
-
-        if (sticky) {
-            lastTemplateLoaderForName.remove(name);
-        }
-        return TemplateLoadingResult.NOT_FOUND;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b0e08ccb/src/main/java/org/apache/freemarker/core/templateresolver/NullCacheStorage.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/templateresolver/NullCacheStorage.java
 
b/src/main/java/org/apache/freemarker/core/templateresolver/NullCacheStorage.java
deleted file mode 100644
index 1f63675..0000000
--- 
a/src/main/java/org/apache/freemarker/core/templateresolver/NullCacheStorage.java
+++ /dev/null
@@ -1,66 +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;
-
-/**
- * A cache storage that doesn't store anything. Use this if you
- * don't want caching.
- *
- * @see org.apache.freemarker.core.Configuration#setCacheStorage(CacheStorage)
- * 
- * @since 2.3.17
- */
-public class NullCacheStorage implements ConcurrentCacheStorage, 
CacheStorageWithGetSize {
-    
-    /**
-     * @since 2.3.22
-     */
-    public static final NullCacheStorage INSTANCE = new NullCacheStorage();
-
-    public boolean isConcurrent() {
-        return true;
-    }
-    
-    public Object get(Object key) {
-        return null;
-    }
-
-    public void put(Object key, Object value) {
-        // do nothing
-    }
-
-    public void remove(Object key) {
-        // do nothing
-    }
-    
-    public void clear() {
-        // do nothing
-    }
-
-    /**
-     * Always returns 0.
-     * 
-     * @since 2.3.21
-     */
-    public int getSize() {
-        return 0;
-    }
-    
-}

Reply via email to