/*
 * 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.util.Locale;

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

Finds the TemplateLoader-level (storage-level) template source for the template name with which the template was requested (as in Configuration.getTemplate(String)). This usually means trying various TemplateLoader-level template names (so called source names; see also Template.getSourceName()) that were deduced from the requested name. Trying a name usually means calling TemplateLookupContext.lookupWithAcquisitionStrategy(String) with it and checking the value of TemplateLookupResult.isPositive().

Before you write your own lookup strategy, know that:

  • A template lookup strategy meant to operate solely with template names, not with TemplateLoader-s directly. Basically, it's a mapping between the template names that templates and API-s like Configuration.getTemplate(String) see, and those that the underlying TemplateLoader sees.
  • A template lookup strategy doesn't influence the template's name (Template.getName()), which is the normalized form of the template name as it was requested (with Configuration.getTemplate(String), etc.). It only influences the so called source name of the template (Template.getSourceName()). The template's name is used as the basis for resolving relative inclusions/imports in the template. The source name is pretty much only used in error messages as error location, and of course, to actually load the template "file".
  • Understand the impact of the last point if your template lookup strategy fiddles not only with the file name part of the template name, but also with the directory part. For example, one may want to map "foo.ftl" to "en/foo.ftl", "fr/foo.ftl", etc. That's legal, but the result is kind of like if you had several root directories ("en/", "fr/", etc.) that are layered over each other to form a single merged directory. (This is what's desirable in typical applications, yet it can be confusing.)
See Also:
Since:2.3.22
/** * Finds the {@link TemplateLoader}-level (storage-level) template source for the template name with which the template * was requested (as in {@link Configuration#getTemplate(String)}). This usually means trying various * {@link TemplateLoader}-level template names (so called source names; see also {@link Template#getSourceName()}) that * were deduced from the requested name. Trying a name usually means calling * {@link TemplateLookupContext#lookupWithAcquisitionStrategy(String)} with it and checking the value of * {@link TemplateLookupResult#isPositive()}. * * <p> * Before you write your own lookup strategy, know that: * <ul> * <li>A template lookup strategy meant to operate solely with template names, not with {@link TemplateLoader}-s * directly. Basically, it's a mapping between the template names that templates and API-s like * {@link Configuration#getTemplate(String)} see, and those that the underlying {@link TemplateLoader} sees. * <li>A template lookup strategy doesn't influence the template's name ({@link Template#getName()}), which is the * normalized form of the template name as it was requested (with {@link Configuration#getTemplate(String)}, etc.). It * only influences the so called source name of the template ({@link Template#getSourceName()}). The template's name is * used as the basis for resolving relative inclusions/imports in the template. The source name is pretty much only used * in error messages as error location, and of course, to actually load the template "file". * <li>Understand the impact of the last point if your template lookup strategy fiddles not only with the file name part * of the template name, but also with the directory part. For example, one may want to map "foo.ftl" to "en/foo.ftl", * "fr/foo.ftl", etc. That's legal, but the result is kind of like if you had several root directories ("en/", "fr/", * etc.) that are layered over each other to form a single merged directory. (This is what's desirable in typical * applications, yet it can be confusing.) * </ul> * * @see Configuration#setTemplateLookupStrategy(TemplateLookupStrategy) * * @since 2.3.22 */
public abstract class TemplateLookupStrategy {

The default lookup strategy of FreeMarker.

Through an example: Assuming localized lookup is enabled and that a template is requested for the name example.ftl and Locale("es", "ES", "Traditional_WIN"), it will try the following template names, in this order: "foo_en_AU_Traditional_WIN.ftl", "foo_en_AU_Traditional.ftl", "foo_en_AU.ftl", "foo_en.ftl", "foo.ftl". It stops at the first variation where it finds a template. (If the template name contains "*" steps, finding the template for the attempted localized variation happens with the template acquisition mechanism.) If localized lookup is disabled, it won't try to add any locale strings, so it just looks for "foo.ftl".

The generation of the localized name variation with the default lookup strategy, happens like this: It removes the file extension (the part starting with the last dot), then appends Locale.toString() after it, and puts back the extension. Then it starts to remove the parts from the end of the locale, considering "_" as the separator between the parts. It won't remove parts that are not part of the locale string (like if the requested template name is foo_bar.ftl, it won't remove the "_bar").

/** * <p> * The default lookup strategy of FreeMarker. * * <p> * Through an example: Assuming localized lookup is enabled and that a template is requested for the name * {@code example.ftl} and {@code Locale("es", "ES", "Traditional_WIN")}, it will try the following template names, * in this order: {@code "foo_en_AU_Traditional_WIN.ftl"}, {@code "foo_en_AU_Traditional.ftl"}, * {@code "foo_en_AU.ftl"}, {@code "foo_en.ftl"}, {@code "foo.ftl"}. It stops at the first variation where it finds * a template. (If the template name contains "*" steps, finding the template for the attempted localized variation * happens with the template acquisition mechanism.) If localized lookup is disabled, it won't try to add any locale * strings, so it just looks for {@code "foo.ftl"}. * * <p> * The generation of the localized name variation with the default lookup strategy, happens like this: It removes * the file extension (the part starting with the <em>last</em> dot), then appends {@link Locale#toString()} after * it, and puts back the extension. Then it starts to remove the parts from the end of the locale, considering * {@code "_"} as the separator between the parts. It won't remove parts that are not part of the locale string * (like if the requested template name is {@code foo_bar.ftl}, it won't remove the {@code "_bar"}). */
public static final TemplateLookupStrategy DEFAULT_2_3_0 = new Default020300();
Finds the template source that matches the template name, locale (if not null) and other parameters specified in the TemplateLookupContext. See also the class-level TemplateLookupStrategy documentation to understand lookup strategies more.
Params:
Returns:Usually the return value of TemplateLookupContext.lookupWithAcquisitionStrategy(String), or TemplateLookupContext#createNegativeLookupResult() if no matching template exists. Can't be null.
/** * Finds the template source that matches the template name, locale (if not {@code null}) and other parameters * specified in the {@link TemplateLookupContext}. See also the class-level {@link TemplateLookupStrategy} * documentation to understand lookup strategies more. * * @param ctx * Contains the parameters for which the matching template need to be found, and operations that * are needed to implement the strategy. Some of the important input parameters are: * {@link TemplateLookupContext#getTemplateName()}, {@link TemplateLookupContext#getTemplateLocale()}. * The most important operations are {@link TemplateLookupContext#lookupWithAcquisitionStrategy(String)} * and {@link TemplateLookupContext#createNegativeLookupResult()}. (Note that you deliberately can't * use {@link TemplateLoader}-s directly to implement lookup.) * * @return Usually the return value of {@link TemplateLookupContext#lookupWithAcquisitionStrategy(String)}, or * {@code TemplateLookupContext#createNegativeLookupResult()} if no matching template exists. Can't be * {@code null}. */
public abstract TemplateLookupResult lookup(TemplateLookupContext ctx) throws IOException; private static class Default020300 extends TemplateLookupStrategy { @Override public TemplateLookupResult lookup(TemplateLookupContext ctx) throws IOException { return ctx.lookupWithLocalizedThenAcquisitionStrategy(ctx.getTemplateName(), ctx.getTemplateLocale()); } @Override public String toString() { return "TemplateLookupStrategy.DEFAULT_2_3_0"; } } }