/*
 * 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 freemarker.cache;

import java.io.IOException;
import java.io.Reader;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateNotFoundException;

/**
 * FreeMarker loads template "files" through objects that implement this interface, thus the templates need not be real
 * files, and can come from any kind of data source (like classpath, servlet context, database, etc). While FreeMarker
 * provides a few {@link TemplateLoader} implementations out-of-the-box, it's normal for embedding frameworks to use
 * their own implementations.
 *
 * <p>To set the {@link TemplateLoader} used by FreeMaker, use
 * {@link Configuration#setTemplateLoader(TemplateLoader)}.
 *
 * <p>Implementations of this interface should be thread-safe.
 *
 * <p>Implementations should override {@link Object#toString()} to show information about from where the
 * {@link TemplateLoader} loads the templates. For example, for a template loader that loads template from database
 * table {@code toString} could return something like
 * {@code "MyDatabaseTemplateLoader(user=\"cms\", table=\"mail_templates\")"}. This string will be shown in
 * {@link TemplateNotFoundException} exception messages, next to the template name.
 *
 * <p>For those who has to dig deeper, note that the {@link TemplateLoader} is actually stored inside
 * the {@link TemplateCache} of the {@link Configuration}, and is normally only accessed directly by the
 * {@link TemplateCache}, and templates are get via the {@link TemplateCache} API-s.
 *
 * <p>SECURITY WARNING: The contract of this interface does not require checking the template name against
 * directory traversal attacks! If users or external systems can specify any part of the template name (of the template
 * path), ensure that it's restricted in format (like ban special characters, ensure that you have a template file
 * extension at the end), or check if the {@link TemplateLoader} you are using checks against path traversal attacks. In
 * principle, normalizing template names and basic traversal limitation is the responsibility of the
 * {@link TemplateCache}, however, not everyone gets {@link Template}-s through
 * {@link Configuration#getTemplate(String)}, and so through {@link TemplateCache}. Also, a concrete
 * {@link TemplateLoader} implementation can have storage-specific syntax that {@link TemplateCache} doesn't recognize,
 * but allows path traversal.
 */
public interface TemplateLoader {

    /**
     * Finds the template in the backing storage and returns an object that identifies the storage location where the
     * template can be loaded from. See the return value for more information.
     *
     * @param name
     *         The name (template root directory relative path) of the template, already localized and normalized by the
     *         {@link freemarker.cache.TemplateCache cache}. It is completely up to the loader implementation to
     *         interpret the name, however it should expect to receive hierarchical paths where path components are
     *         separated by a slash (not backslash). Backslashes (or any other OS specific separator character) are not
     *         considered as separators by FreeMarker, and thus they will not be replaced with slash before passing to
     *         this method, so it's up to the template loader to handle them (say, by throwing an exception that tells
     *         the user that the path (s)he has entered is invalid, as (s)he must use slash -- typical mistake of
     *         Windows users). The passed names are always considered relative to some loader-defined root location
     *         (often referred as the "template root directory"), and will never start with a slash, nor will they
     *         contain a path component consisting of either a single or a double dot -- these are all resolved by the
     *         template cache before passing the name to the loader. As a side effect, paths that trivially reach
     *         outside template root directory, such as {@code ../my.ftl}, will be rejected by the template cache, so
     *         they never reach the template loader. Note again, that if the path uses backslash as path separator
     *         instead of slash as (the template loader should not accept that), the normalization will not properly
     *         happen, as FreeMarker (the cache) recognizes only the slashes as separators.
     *
     * @return An object representing the template source, which can be supplied in subsequent calls to
     * {@link #getLastModified(Object)} and {@link #getReader(Object, String)}, when those are called on the same
     * {@link TemplateLoader}. {@code null} must be returned if the source for the template doesn't exist; don't throw
     * exception then! The exact type of this object is up to the {@link TemplateLoader} implementation. As this object
     * is possibly used as hash key in caches, and is surly compared with another template source for equality, <b>it
     * must have a proper {@link Object#equals(Object)} and {@link Object#hashCode()}) implementation</b>. Especially,
     * template sources that refer to the same physical source must be equivalent, otherwise template caching can become
     * inefficient. This is only expected from {@link Object#equals(Object)} when the compared template sources came
     * from the same {@link TemplateLoader} instance. Also, it must not influence the equality if the source is open or
     * closed ({@link #closeTemplateSource(Object)}).
     *
     * @throws IOException
     *         When an error occurs that makes it impossible to find out if the template exists, or to access the
     *         existing template. Don't throw exception if the template doesn't exist, instead return with {@code null}
     *         then!
     */
    Object findTemplateSource(String name) throws IOException;

    /**
     * Returns the time of last modification of the specified template source. This method is called after
     * <code>findTemplateSource()</code>.
     *
     * @param templateSource
     *         an object representing a template source (the template file), obtained through a prior call to
     *         {@link #findTemplateSource(String)}. This must be an object on which
     *         {@link TemplateLoader#closeTemplateSource(Object)} wasn't applied yet.
     *
     * @return The time of last modification for the specified template source, or -1 if the time is not known. This
     * value meant to be milliseconds since the epoch, but in fact FreeMarker doesn't care what it means, it only cares
     * if it changes, in which case the template needs to be reloaded (even if the value has decreased). -1 is not
     * special in that regard either; if you keep returning it, FreeMarker won't reload the template (as far as it's not
     * evicted from the cache from some other reason). Note that {@link Long#MIN_VALUE} is reserved for internal use.
     */
    long getLastModified(Object templateSource);

    /**
     * Returns the character stream of a template represented by the specified template source. This method is possibly
     * called for multiple times for the same template source object, and it must always return a {@link Reader} that
     * reads the template from its beginning. Before this method is called for the second time (or later), its caller
     * must close the previously returned {@link Reader}, and it must not use it anymore. That is, this method is not
     * required to support multiple concurrent readers for the same source {@code templateSource} object.
     *
     * <p>
     * Typically, this method is called if the template is missing from the cache, or if after calling
     * {@link #findTemplateSource(String)} and {@link #getLastModified(Object)} it was determined that the cached copy
     * of the template is stale. Then, if it turns out that the {@code encoding} parameter used doesn't match the actual
     * template content (based on the {@code #ftl encoding=...} header), this method will be called for a second time
     * with the correct {@code encoding} parameter value.
     *
     * <p>
     * Unlike {@link #findTemplateSource(String)}, this method must not tolerate if the template is not found, and must
     * throw {@link IOException} in that case.
     *
     * @param templateSource
     *         an object representing a template source, obtained through a prior call to
     *         {@link #findTemplateSource(String)}. This must be an object on which
     *         {@link TemplateLoader#closeTemplateSource(Object)} wasn't applied yet.
     * @param encoding
     *         the character encoding used to translate source bytes to characters. Some loaders may not have access to
     *         the byte representation of the template stream, and instead directly obtain a character stream. These
     *         loaders should ignore the encoding parameter.
     *
     * @return A {@link Reader} representing the template character stream; not {@code null}. It's the responsibility of
     * the caller (which is {@link TemplateCache} usually) to {@code close()} it. The {@link Reader} is not required to
     * work after the {@code templateSource} was closed ({@link #closeTemplateSource(Object)}).
     *
     * @throws IOException
     *         if an I/O error occurs while accessing the stream.
     */
    Reader getReader(Object templateSource, String encoding) throws IOException;

    /**
     * Closes the template source, releasing any resources held that are only required for reading the template and/or
     * its metadata. This is the last method that is called by the {@link TemplateCache} for a template source, except
     * that {@link Object#equals(Object)} is might called later too. {@link TemplateCache} ensures that this method will
     * be called on every object that is returned from {@link #findTemplateSource(String)}.
     *
     * @param templateSource
     *         the template source that should be closed.
     */
    void closeTemplateSource(Object templateSource) throws IOException;

}
