blob: 6b50aa950949a787c2a67a4f1685fe9cedaf8eb5 [file] [log] [blame]
/*
* 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 {@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 {
/**
* <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 {@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";
}
}
}