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

import java.io.BufferedReader;
import java.io.FilterReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;

import javax.swing.tree.TreePath;

import freemarker.cache.TemplateCache;
import freemarker.cache.TemplateLoader;
import freemarker.cache.TemplateLookupStrategy;
import freemarker.core.BugException;
import freemarker.core.Configurable;
import freemarker.core.Environment;
import freemarker.core.FMParser;
import freemarker.core.LibraryLoad;
import freemarker.core.Macro;
import freemarker.core.OutputFormat;
import freemarker.core.ParseException;
import freemarker.core.ParserConfiguration;
import freemarker.core.TemplateConfiguration;
import freemarker.core.TemplateElement;
import freemarker.core.TextBlock;
import freemarker.core.TokenMgrError;
import freemarker.core._CoreAPI;
import freemarker.debug.impl.DebuggerService;

Stores an already parsed template, ready to be processed (rendered) for unlimited times, possibly from multiple threads.

Typically, you will use Configuration.getTemplate(String) to create/get Template objects, so you don't construct them directly. But you can also construct a template from a Reader or a String that contains the template source code. But then it's important to know that while the resulting Template is efficient for later processing, creating a new Template itself is relatively expensive. So try to re-use Template objects if possible. Configuration.getTemplate(String) (and its overloads) does that (caching Template-s) for you, but the constructor of course doesn't, so it's up to you to solve then.

Objects of this class meant to be handled as immutable and thus thread-safe. However, it has some setter methods for changing FreeMarker settings. Those must not be used while the template is being processed, or if the template object is already accessible from multiple threads. If some templates need different settings that those coming from the shared Configuration, and you are using Configuration.getTemplate(String) (or its overloads), then use Configuration.setTemplateConfigurations(TemplateConfigurationFactory) to achieve that.

/** * <p> * Stores an already parsed template, ready to be processed (rendered) for unlimited times, possibly from multiple * threads. * * <p> * Typically, you will use {@link Configuration#getTemplate(String)} to create/get {@link Template} objects, so you * don't construct them directly. But you can also construct a template from a {@link Reader} or a {@link String} that * contains the template source code. But then it's important to know that while the resulting {@link Template} is * efficient for later processing, creating a new {@link Template} itself is relatively expensive. So try to re-use * {@link Template} objects if possible. {@link Configuration#getTemplate(String)} (and its overloads) does that * (caching {@link Template}-s) for you, but the constructor of course doesn't, so it's up to you to solve then. * * <p> * Objects of this class meant to be handled as immutable and thus thread-safe. However, it has some setter methods for * changing FreeMarker settings. Those must not be used while the template is being processed, or if the template object * is already accessible from multiple threads. If some templates need different settings that those coming from the * shared {@link Configuration}, and you are using {@link Configuration#getTemplate(String)} (or its overloads), then * use {@link Configuration#setTemplateConfigurations(freemarker.cache.TemplateConfigurationFactory)} to achieve that. */
public class Template extends Configurable { public static final String DEFAULT_NAMESPACE_PREFIX = "D"; public static final String NO_NS_PREFIX = "N"; private static final int READER_BUFFER_SIZE = 4096; private Map macros = new HashMap(); private List imports = new Vector(); private TemplateElement rootElement; private String encoding, defaultNS; private Object customLookupCondition; private int interpolationSyntax; private int actualTagSyntax; private int actualNamingConvention; private boolean autoEscaping; private OutputFormat outputFormat; private final String name; private final String sourceName; private final ArrayList lines = new ArrayList(); private final ParserConfiguration parserConfiguration; private Map prefixToNamespaceURILookup = new HashMap(); private Map namespaceURIToPrefixLookup = new HashMap(); private Version templateLanguageVersion;
A prime constructor to which all other constructors should delegate directly or indirectly.
/** * A prime constructor to which all other constructors should * delegate directly or indirectly. */
private Template(String name, String sourceName, Configuration cfg, ParserConfiguration customParserConfiguration) { super(toNonNull(cfg)); this.name = name; this.sourceName = sourceName; this.templateLanguageVersion = normalizeTemplateLanguageVersion(toNonNull(cfg).getIncompatibleImprovements()); this.parserConfiguration = customParserConfiguration != null ? customParserConfiguration : getConfiguration(); } private static Configuration toNonNull(Configuration cfg) { return cfg != null ? cfg : Configuration.getDefaultConfiguration(); }
Same as Template(String, String, Reader, Configuration) with null sourceName parameter.
/** * Same as {@link #Template(String, String, Reader, Configuration)} with {@code null} {@code sourceName} parameter. */
public Template(String name, Reader reader, Configuration cfg) throws IOException { this(name, null, reader, cfg); }
Since:2.3.20
/** * Convenience constructor for {@link #Template(String, Reader, Configuration) * Template(name, new StringReader(reader), cfg)}. * * @since 2.3.20 */
public Template(String name, String sourceCode, Configuration cfg) throws IOException { this(name, new StringReader(sourceCode), cfg); }
Convenience constructor for Template(name, null, reader, cfg, encoding).
/** * Convenience constructor for {@link #Template(String, String, Reader, Configuration, String) Template(name, null, * reader, cfg, encoding)}. */
public Template(String name, Reader reader, Configuration cfg, String encoding) throws IOException { this(name, null, reader, cfg, encoding); }
Constructs a template from a character stream. Note that this is a relatively expensive operation; where higher performance matters, you should re-use (cache) Template instances instead of re-creating them from the same source again and again. (and its overloads already do such reuse.)
Params:
  • name – The path of the template file relatively to the (virtual) directory that you use to store the templates (except if sourceName differs from it). Shouldn't start with '/'. Should use '/', not '\'. Check getName() to see how the name will be used. The name should be independent of the actual storage mechanism and physical location as far as possible. Even when the templates are stored straightforwardly in real files (they often aren't; see TemplateLoader), the name shouldn't be an absolute file path. Like if the template is stored in "/www/templates/forum/main.ftl", and you are using "/www/templates/" as the template root directory via Configuration.setDirectoryForTemplateLoading(File), then the template name will be "forum/main.ftl". The name can be null (should be used for template made on-the-fly instead of being loaded from somewhere), in which case relative paths in it will be relative to the template root directory (and here again, it's the TemplateLoader that knows what that "physically" means).
  • sourceName – See getSourceName() for the meaning. Can be null, in which case getSourceName() will return the same as getName().
  • reader – The character stream to read from. It will always be closed (Reader.close()) by this method (this is for backward compatibility; later major versions may discontinue this behavior). The Reader need not be buffered, because this method ensures that it will be read in few kilobyte chunks, not byte by byte.
  • cfg – The Configuration object that this Template is associated with. If this is null, the "default" Configuration object is used, which is highly discouraged, because it can easily lead to erroneous, unpredictable behavior. (See more here...)
Since:2.3.22
/** * Constructs a template from a character stream. Note that this is a relatively expensive operation; where higher * performance matters, you should re-use (cache) {@link Template} instances instead of re-creating them from the * same source again and again. ({@link Configuration#getTemplate(String) and its overloads already do such reuse.}) * * @param name * The path of the template file relatively to the (virtual) directory that you use to store the * templates (except if {@link #Template(String, String, Reader, Configuration, String) sourceName} * differs from it). Shouldn't start with {@code '/'}. Should use {@code '/'}, not {@code '\'}. Check * {@link #getName()} to see how the name will be used. The name should be independent of the actual * storage mechanism and physical location as far as possible. Even when the templates are stored * straightforwardly in real files (they often aren't; see {@link TemplateLoader}), the name shouldn't be * an absolute file path. Like if the template is stored in {@code "/www/templates/forum/main.ftl"}, and * you are using {@code "/www/templates/"} as the template root directory via * {@link Configuration#setDirectoryForTemplateLoading(java.io.File)}, then the template name will be * {@code "forum/main.ftl"}. The name can be {@code null} (should be used for template made on-the-fly * instead of being loaded from somewhere), in which case relative paths in it will be relative to * the template root directory (and here again, it's the {@link TemplateLoader} that knows what that * "physically" means). * @param sourceName * See {@link #getSourceName()} for the meaning. Can be {@code null}, in which case * {@link #getSourceName()} will return the same as {@link #getName()}. * @param reader * The character stream to read from. It will always be closed ({@link Reader#close()}) by * this method (this is for backward compatibility; later major versions may discontinue this behavior). * The {@link Reader} need not be buffered, because this method ensures that it will be read in few * kilobyte chunks, not byte by byte. * @param cfg * The Configuration object that this Template is associated with. If this is {@code null}, the "default" * {@link Configuration} object is used, which is highly discouraged, because it can easily lead to * erroneous, unpredictable behavior. (See more {@link Configuration#getDefaultConfiguration() here...}) * * @since 2.3.22 */
public Template( String name, String sourceName, Reader reader, Configuration cfg) throws IOException { this(name, sourceName, reader, cfg, null); }
Same as Template(String, String, Reader, Configuration), but also specifies the template's encoding (not recommended).
Params:
  • encoding – This is the encoding that we are supposed to be using. At the first glance it's unnecessary because we already have a Reader (so decoding with the charset has already happened), however, if this is non-null and there's an #ftl header with encoding parameter, they must match, or else a WrongEncodingException is thrown. Thus, it should be set if to decode the template, we were using an encoding (a charset), otherwise it should be null. It's also kept as meta-info (returned by getEncoding()). It also has an impact when #include-ing or #import-ing another template from this template, as its default encoding will be this. But this behavior of said directives is considered to be harmful, and will be probably phased out.
Since:2.3.22
/** * Same as {@link #Template(String, String, Reader, Configuration)}, but also specifies the template's encoding (not * recommended). * * @param encoding * This is the encoding that we are supposed to be using. At the first glance it's unnecessary because we * already have a {@link Reader} (so decoding with the charset has already happened), however, if this is * non-{@code null} and there's an {@code #ftl} header with {@code encoding} parameter, they must match, * or else a {@link WrongEncodingException} is thrown. Thus, it should be set if to decode the template, * we were using an encoding (a charset), otherwise it should be {@code null}. It's also kept as * meta-info (returned by {@link #getEncoding()}). It also has an impact when {@code #include}-ing or * {@code #import}-ing another template from this template, as its default encoding will be this. But * this behavior of said directives is considered to be harmful, and will be probably phased out. * * @since 2.3.22 */
public Template( String name, String sourceName, Reader reader, Configuration cfg, String encoding) throws IOException { this(name, sourceName, reader, cfg, null, encoding); }
Same as Template(String, String, Reader, Configuration, String), but also specifies a TemplateConfiguration. This is mostly meant to be used by FreeMarker internally, but advanced users might still find this useful.
Params:
Since:2.3.24
/** * Same as {@link #Template(String, String, Reader, Configuration, String)}, but also specifies a * {@link TemplateConfiguration}. This is mostly meant to be used by FreeMarker internally, but advanced users might * still find this useful. * * @param customParserConfiguration * Overrides the parsing related configuration settings of the {@link Configuration} parameter; can be * {@code null}. This is useful as the {@link Configuration} is normally a singleton shared by all * templates, and so it's not good for specifying template-specific settings. (While {@link Template} * itself has methods to specify settings just for that template, those don't influence the parsing, and * you only have opportunity to call them after the parsing anyway.) This objects is often a * {@link TemplateConfiguration} whose parent is the {@link Configuration} parameter, and then it * practically just overrides some of the parser settings, as the others are inherited from the * {@link Configuration}. Note that if this is a {@link TemplateConfiguration}, you will also want to * call {@link TemplateConfiguration#apply(Template)} on the resulting {@link Template} so that * {@link Configurable} settings will be set too, because this constructor only uses it as a * {@link ParserConfiguration}. * @param encoding * Same as in {@link #Template(String, String, Reader, Configuration, String)}. When it's non-{@code * null}, it overrides the value coming from the {@link TemplateConfiguration#getEncoding()} method of * the {@code templateConfiguration} parameter. * * @since 2.3.24 */
public Template( String name, String sourceName, Reader reader, Configuration cfg, ParserConfiguration customParserConfiguration, String encoding) throws IOException { this(name, sourceName, cfg, customParserConfiguration); this.setEncoding(encoding); LineTableBuilder ltbReader; try { ParserConfiguration actualParserConfiguration = getParserConfiguration(); if (!(reader instanceof BufferedReader) && !(reader instanceof StringReader)) { reader = new BufferedReader(reader, READER_BUFFER_SIZE); } ltbReader = new LineTableBuilder(reader, actualParserConfiguration); reader = ltbReader; try { FMParser parser = new FMParser(this, reader, actualParserConfiguration); if (cfg != null) { _CoreAPI.setPreventStrippings(parser, cfg.getPreventStrippings()); } try { this.rootElement = parser.Root(); } catch (IndexOutOfBoundsException exc) { // There's a JavaCC bug where the Reader throws a RuntimeExcepton and then JavaCC fails with // IndexOutOfBoundsException. If that wasn't the case, we just rethrow. Otherwise we suppress the // IndexOutOfBoundsException and let the real cause to be thrown later. if (!ltbReader.hasFailure()) { throw exc; } rootElement = null; } this.actualTagSyntax = parser._getLastTagSyntax(); this.interpolationSyntax = actualParserConfiguration.getInterpolationSyntax(); this.actualNamingConvention = parser._getLastNamingConvention(); } catch (TokenMgrError exc) { // TokenMgrError VS ParseException is not an interesting difference for the user, so we just convert it // to ParseException throw exc.toParseException(this); } } catch (ParseException e) { e.setTemplateName(getSourceName()); throw e; } finally { reader.close(); } // Throws any exception that JavaCC has silently treated as EOF: ltbReader.throwFailure(); DebuggerService.registerTemplate(this); namespaceURIToPrefixLookup = Collections.unmodifiableMap(namespaceURIToPrefixLookup); prefixToNamespaceURILookup = Collections.unmodifiableMap(prefixToNamespaceURILookup); }
Deprecated:This constructor uses the "default" Configuration instance, which can easily lead to erroneous, unpredictable behavior. See more here....
/** * Equivalent to {@link #Template(String, Reader, Configuration) * Template(name, reader, null)}. * * @deprecated This constructor uses the "default" {@link Configuration} * instance, which can easily lead to erroneous, unpredictable behavior. * See more {@link Configuration#getDefaultConfiguration() here...}. */
@Deprecated public Template(String name, Reader reader) throws IOException { this(name, reader, (Configuration) null); }
Only meant to be used internally.
Deprecated:Has problems setting actualTagSyntax and templateLanguageVersion; will be removed in 2.4.
/** * Only meant to be used internally. * * @deprecated Has problems setting actualTagSyntax and templateLanguageVersion; will be removed in 2.4. */
@Deprecated // [2.4] remove this Template(String name, TemplateElement root, Configuration cfg) { this(name, null, cfg, (ParserConfiguration) null); this.rootElement = root; DebuggerService.registerTemplate(this); }
Same as getPlainTextTemplate(String, String, String, Configuration) with null sourceName argument.
/** * Same as {@link #getPlainTextTemplate(String, String, String, Configuration)} with {@code null} {@code sourceName} * argument. */
static public Template getPlainTextTemplate(String name, String content, Configuration config) { return getPlainTextTemplate(name, null, content, config); }
Creates (not "get"-s) a Template that only contains a single block of static text, no dynamic content.
Params:
  • name – See getName for more details.
  • sourceName – See getSourceName for more details. If null, it will be the same as the name.
  • content – the block of text that this template represents
  • config – the configuration to which this template belongs
Since:2.3.22
/** * Creates (not "get"-s) a {@link Template} that only contains a single block of static text, no dynamic content. * * @param name * See {@link #getName} for more details. * @param sourceName * See {@link #getSourceName} for more details. If {@code null}, it will be the same as the {@code name}. * @param content * the block of text that this template represents * @param config * the configuration to which this template belongs * * @since 2.3.22 */
static public Template getPlainTextTemplate(String name, String sourceName, String content, Configuration config) { Template template; try { template = new Template(name, sourceName, new StringReader("X"), config); } catch (IOException e) { throw new BugException("Plain text template creation failed", e); } _CoreAPI.replaceText((TextBlock) template.rootElement, content); DebuggerService.registerTemplate(template); return template; } private static Version normalizeTemplateLanguageVersion(Version incompatibleImprovements) { _TemplateAPI.checkVersionNotNullAndSupported(incompatibleImprovements); int v = incompatibleImprovements.intValue(); if (v < _TemplateAPI.VERSION_INT_2_3_19) { return Configuration.VERSION_2_3_0; } else if (v > _TemplateAPI.VERSION_INT_2_3_21) { return Configuration.VERSION_2_3_21; } else { // if 2.3.19 or 2.3.20 or 2.3.21 return incompatibleImprovements; } }
Executes template, using the data-model provided, writing the generated output to the supplied Writer.

For finer control over the runtime environment setup, such as per-HTTP-request configuring of FreeMarker settings, you may need to use createProcessingEnvironment(Object, Writer) instead.

Params:
  • dataModel – the holder of the variables visible from the template (name-value pairs); usually a Map<String, Object> or a JavaBean (where the JavaBean properties will be the variables). Can be any object that the ObjectWrapper in use turns into a TemplateHashModel. You can also use an object that already implements TemplateHashModel; in that case it won't be wrapped. If it's null, an empty data model is used.
  • out – The Writer where the output of the template will go. Note that unless you have used Configurable.setAutoFlush(boolean) to disable this, Writer.flush() will be called at the when the template processing was finished. Writer.close() is not called. Can't be null.
Throws:
  • TemplateException – if an exception occurs during template processing
  • IOException – if an I/O exception occurs during writing to the writer.
/** * Executes template, using the data-model provided, writing the generated output to the supplied {@link Writer}. * * <p> * For finer control over the runtime environment setup, such as per-HTTP-request configuring of FreeMarker * settings, you may need to use {@link #createProcessingEnvironment(Object, Writer)} instead. * * @param dataModel * the holder of the variables visible from the template (name-value pairs); usually a * {@code Map<String, Object>} or a JavaBean (where the JavaBean properties will be the variables). Can * be any object that the {@link ObjectWrapper} in use turns into a {@link TemplateHashModel}. You can * also use an object that already implements {@link TemplateHashModel}; in that case it won't be * wrapped. If it's {@code null}, an empty data model is used. * @param out * The {@link Writer} where the output of the template will go. Note that unless you have used * {@link Configuration#setAutoFlush(boolean)} to disable this, {@link Writer#flush()} will be called at * the when the template processing was finished. {@link Writer#close()} is not called. Can't be * {@code null}. * * @throws TemplateException * if an exception occurs during template processing * @throws IOException * if an I/O exception occurs during writing to the writer. */
public void process(Object dataModel, Writer out) throws TemplateException, IOException { createProcessingEnvironment(dataModel, out, null).process(); }
Like process(Object, Writer), but also sets a (XML-)node to be recursively processed by the template. That node is accessed in the template with .node, #recurse, etc. See the Declarative XML Processing as a typical example of recursive node processing.
Params:
  • rootNode – The root node for recursive processing or null.
Throws:
  • TemplateException – if an exception occurs during template processing
  • IOException – if an I/O exception occurs during writing to the writer.
/** * Like {@link #process(Object, Writer)}, but also sets a (XML-)node to be recursively processed by the template. * That node is accessed in the template with <tt>.node</tt>, <tt>#recurse</tt>, etc. See the * <a href="https://freemarker.apache.org/docs/xgui_declarative.html" target="_blank">Declarative XML Processing</a> as a * typical example of recursive node processing. * * @param rootNode The root node for recursive processing or {@code null}. * * @throws TemplateException if an exception occurs during template processing * @throws IOException if an I/O exception occurs during writing to the writer. */
public void process(Object dataModel, Writer out, ObjectWrapper wrapper, TemplateNodeModel rootNode) throws TemplateException, IOException { Environment env = createProcessingEnvironment(dataModel, out, wrapper); if (rootNode != null) { env.setCurrentVisitorNode(rootNode); } env.process(); }
Params:
/** * Like {@link #process(Object, Writer)}, but overrides the {@link Configuration#getObjectWrapper()}. * * @param wrapper The {@link ObjectWrapper} to be used instead of what {@link Configuration#getObjectWrapper()} * provides, or {@code null} if you don't want to override that. */
public void process(Object dataModel, Writer out, ObjectWrapper wrapper) throws TemplateException, IOException { createProcessingEnvironment(dataModel, out, wrapper).process(); }
Creates a Environment object, using this template, the data-model provided as parameter. You have to call Environment.process() on the return value to set off the actual rendering.

Use this method if you want to do some special initialization on the Environment before template processing, or if you want to read the Environment after template processing. Otherwise using process(Object, Writer) is simpler.

Example:

Environment env = myTemplate.createProcessingEnvironment(root, out, null);
env.process();

The above is equivalent with this:

myTemplate.process(root, out);

But with createProcessingEnvironment, you can manipulate the environment before and after the processing:

Environment env = myTemplate.createProcessingEnvironment(root, out);
env.setLocale(myUsersPreferredLocale);
env.setTimeZone(myUsersPreferredTimezone);
env.process();  // output is rendered here
TemplateModel x = env.getVariable("x");  // read back a variable set by the template
Params:
Throws:
  • TemplateException – if an exception occurs while setting up the Environment object.
  • IOException – if an exception occurs doing any auto-imports
Returns:the Environment object created for processing. Call Environment.process() to process the template.
/** * Creates a {@link freemarker.core.Environment Environment} object, using this template, the data-model provided as * parameter. You have to call {@link Environment#process()} on the return value to set off the actual rendering. * * <p>Use this method if you want to do some special initialization on the {@link Environment} before template * processing, or if you want to read the {@link Environment} after template processing. Otherwise using * {@link Template#process(Object, Writer)} is simpler. * * <p>Example: * * <pre> * Environment env = myTemplate.createProcessingEnvironment(root, out, null); * env.process();</pre> * * <p>The above is equivalent with this: * * <pre> * myTemplate.process(root, out);</pre> * * <p>But with <tt>createProcessingEnvironment</tt>, you can manipulate the environment * before and after the processing: * * <pre> * Environment env = myTemplate.createProcessingEnvironment(root, out); * * env.setLocale(myUsersPreferredLocale); * env.setTimeZone(myUsersPreferredTimezone); * * env.process(); // output is rendered here * * TemplateModel x = env.getVariable("x"); // read back a variable set by the template</pre> * * @param dataModel the holder of the variables visible from all templates; see {@link #process(Object, Writer)} for * more details. * @param wrapper The {@link ObjectWrapper} to use to wrap objects into {@link TemplateModel} * instances. Normally you left it {@code null}, in which case {@link Configurable#getObjectWrapper()} will be * used. * @param out The {@link Writer} where the output of the template will go; see {@link #process(Object, Writer)} for * more details. * * @return the {@link Environment} object created for processing. Call {@link Environment#process()} to process the * template. * * @throws TemplateException if an exception occurs while setting up the Environment object. * @throws IOException if an exception occurs doing any auto-imports */
public Environment createProcessingEnvironment(Object dataModel, Writer out, ObjectWrapper wrapper) throws TemplateException, IOException { final TemplateHashModel dataModelHash; if (dataModel instanceof TemplateHashModel) { dataModelHash = (TemplateHashModel) dataModel; } else { if (wrapper == null) { wrapper = getObjectWrapper(); } if (dataModel == null) { dataModelHash = new SimpleHash(wrapper); } else { TemplateModel wrappedDataModel = wrapper.wrap(dataModel); if (wrappedDataModel instanceof TemplateHashModel) { dataModelHash = (TemplateHashModel) wrappedDataModel; } else if (wrappedDataModel == null) { throw new IllegalArgumentException( wrapper.getClass().getName() + " converted " + dataModel.getClass().getName() + " to null."); } else { throw new IllegalArgumentException( wrapper.getClass().getName() + " didn't convert " + dataModel.getClass().getName() + " to a TemplateHashModel. Generally, you want to use a Map<String, Object> or a " + "JavaBean as the root-map (aka. data-model) parameter. The Map key-s or JavaBean " + "property names will be the variable names in the template."); } } } return new Environment(this, dataModelHash, out); } /** * Same as {@link #createProcessingEnvironment(Object, Writer, ObjectWrapper) * createProcessingEnvironment(dataModel, out, null)}. */ public Environment createProcessingEnvironment(Object dataModel, Writer out) throws TemplateException, IOException { return createProcessingEnvironment(dataModel, out, null); }
Returns a string representing the raw template text in canonical form.
/** * Returns a string representing the raw template * text in canonical form. */
@Override public String toString() { StringWriter sw = new StringWriter(); try { dump(sw); } catch (IOException ioe) { throw new RuntimeException(ioe.getMessage()); } return sw.toString(); }
The usually path-like (or URL-like) identifier of the template, or possibly null for non-stored templates. It usually looks like a relative UN*X path; it should use /, not \, and shouldn't start with / (but there are no hard guarantees). It's not a real path in a file-system, it's just a name that a TemplateLoader used to load the backing resource (in simple cases; actually that name is getSourceName(), but see it there). Or, it can also be a name that was never used to load the template (directly created with Template(String, Reader, Configuration)). Even if the templates are stored straightforwardly in files, this is relative to the base directory of the TemplateLoader. So it really could be anything, except that it has importance in these situations:

Relative paths to other templates in this template will be resolved relatively to the directory part of this. Like if the template name is "foo/this.ftl", then <#include "other.ftl"> gets the template with name "foo/other.ftl".

You should not use this name to indicate error locations, or to find the actual templates in general, because localized lookup, acquisition and other lookup strategies can transform names before they get to the TemplateLoader (the template storage) mechanism. Use getSourceName() for these purposes.

Some frameworks use URL-like template names like "someSchema://foo/bar.ftl". FreeMarker understands this notation, so an absolute path like "/baaz.ftl" in that template will be resolved too "someSchema://baaz.ftl".

/** * The usually path-like (or URL-like) identifier of the template, or possibly {@code null} for non-stored * templates. It usually looks like a relative UN*X path; it should use {@code /}, not {@code \}, and shouldn't * start with {@code /} (but there are no hard guarantees). It's not a real path in a file-system, it's just a name * that a {@link TemplateLoader} used to load the backing resource (in simple cases; actually that name is * {@link #getSourceName()}, but see it there). Or, it can also be a name that was never used to load the template * (directly created with {@link #Template(String, Reader, Configuration)}). Even if the templates are stored * straightforwardly in files, this is relative to the base directory of the {@link TemplateLoader}. So it really * could be anything, except that it has importance in these situations: * * <p> * Relative paths to other templates in this template will be resolved relatively to the directory part of this. * Like if the template name is {@code "foo/this.ftl"}, then {@code <#include "other.ftl">} gets the template with * name {@code "foo/other.ftl"}. * </p> * * <p> * You should not use this name to indicate error locations, or to find the actual templates in general, because * localized lookup, acquisition and other lookup strategies can transform names before they get to the * {@link TemplateLoader} (the template storage) mechanism. Use {@link #getSourceName()} for these purposes. * </p> * * <p> * Some frameworks use URL-like template names like {@code "someSchema://foo/bar.ftl"}. FreeMarker understands this * notation, so an absolute path like {@code "/baaz.ftl"} in that template will be resolved too * {@code "someSchema://baaz.ftl"}. */
public String getName() { return name; }
The name that was actually used to load this template from the TemplateLoader (or from other custom storage mechanism). This is what should be shown in error messages as the error location. This is usually the same as getName(), except when localized lookup, template acquisition (* step in the name), or other TemplateLookupStrategy transforms the requested name (getName()) to a different final TemplateLoader-level name. For example, when you get a template with name "foo.ftl" then because of localized lookup, it's possible that something like "foo_en.ftl" will be loaded behind the scenes. While the template name will be still the same as the requested template name ("foo.ftl"), errors should point to "foo_de.ftl". Note that relative paths are always resolved relatively to the name, not to the sourceName.
Since:2.3.22
/** * The name that was actually used to load this template from the {@link TemplateLoader} (or from other custom * storage mechanism). This is what should be shown in error messages as the error location. This is usually the * same as {@link #getName()}, except when localized lookup, template acquisition ({@code *} step in the name), or * other {@link TemplateLookupStrategy} transforms the requested name ({@link #getName()}) to a different final * {@link TemplateLoader}-level name. For example, when you get a template with name {@code "foo.ftl"} then because * of localized lookup, it's possible that something like {@code "foo_en.ftl"} will be loaded behind the scenes. * While the template name will be still the same as the requested template name ({@code "foo.ftl"}), errors should * point to {@code "foo_de.ftl"}. Note that relative paths are always resolved relatively to the {@code name}, not * to the {@code sourceName}. * * @since 2.3.22 */
public String getSourceName() { return sourceName != null ? sourceName : getName(); }
Returns the Configuration object associated with this template.
/** * Returns the Configuration object associated with this template. */
public Configuration getConfiguration() { return (Configuration) getParent(); }
Returns the ParserConfiguration that was used for parsing this template. This is most often the same object as getConfiguration(), but sometimes it's a TemplateConfiguration, or something else. It's never null.
Since:2.3.24
/** * Returns the {@link ParserConfiguration} that was used for parsing this template. This is most often the same * object as {@link #getConfiguration()}, but sometimes it's a {@link TemplateConfiguration}, or something else. * It's never {@code null}. * * @since 2.3.24 */
public ParserConfiguration getParserConfiguration() { return parserConfiguration; }
Return the template language (FTL) version used by this template. For now (2.3.21) this is the same as Configuration.getIncompatibleImprovements(), except that it's normalized to the lowest version where the template language was changed.
/** * Return the template language (FTL) version used by this template. * For now (2.3.21) this is the same as {@link Configuration#getIncompatibleImprovements()}, except * that it's normalized to the lowest version where the template language was changed. */
Version getTemplateLanguageVersion() { return templateLanguageVersion; }
Params:
  • encoding – The encoding that was used to read this template. When this template #include-s or #import-s another template, by default it will use this encoding for those. For backward compatibility, this can be null, which will unset this setting.
Deprecated:Should only be used internally, and might will be removed later.
/** * @param encoding * The encoding that was used to read this template. When this template {@code #include}-s or * {@code #import}-s another template, by default it will use this encoding for those. For backward * compatibility, this can be {@code null}, which will unset this setting. * * @deprecated Should only be used internally, and might will be removed later. */
@Deprecated public void setEncoding(String encoding) { this.encoding = encoding; }
The encoding that was (allegedly) used to read this template; also the the default character encoding used for reading files included from this template. Possibly null, in which case you are supposed to use Configuration.getEncoding(Locale).
/** * The encoding that was (allegedly) used to read this template; also the the default character encoding used for * reading files included from this template. Possibly {@code null}, in which case you are supposed to use * {@link Configuration#getEncoding(Locale)}. */
public String getEncoding() { return this.encoding; }
Gets the custom lookup condition with which this template was found. See the customLookupCondition parameter of Configuration.getTemplate(String, Locale, Object, String, boolean, boolean) for more explanation.
Since:2.3.22
/** * Gets the custom lookup condition with which this template was found. See the {@code customLookupCondition} * parameter of {@link Configuration#getTemplate(String, java.util.Locale, Object, String, boolean, boolean)} for * more explanation. * * @since 2.3.22 */
public Object getCustomLookupCondition() { return customLookupCondition; }
Mostly only used internally; setter pair of getCustomLookupCondition(). This meant to be called directly after instantiating the template with its constructor, after a successfull lookup that used this condition. So this should only be called from code that deals with creating new Template objects, like from TemplateCache.
Since:2.3.22
/** * Mostly only used internally; setter pair of {@link #getCustomLookupCondition()}. This meant to be called directly * after instantiating the template with its constructor, after a successfull lookup that used this condition. So * this should only be called from code that deals with creating new {@code Template} objects, like from * {@link TemplateCache}. * * @since 2.3.22 */
public void setCustomLookupCondition(Object customLookupCondition) { this.customLookupCondition = customLookupCondition; }
Returns the tag syntax the parser has chosen for this template. If the syntax could be determined, it's Configuration.SQUARE_BRACKET_TAG_SYNTAX or Configuration.ANGLE_BRACKET_TAG_SYNTAX. If the syntax couldn't be determined (like because there was no tags in the template, or it was a plain text template), this returns whatever the default is in the current configuration, so it's maybe Configuration.AUTO_DETECT_TAG_SYNTAX.
See Also:
Since:2.3.20
/** * Returns the tag syntax the parser has chosen for this template. If the syntax could be determined, it's * {@link Configuration#SQUARE_BRACKET_TAG_SYNTAX} or {@link Configuration#ANGLE_BRACKET_TAG_SYNTAX}. If the syntax * couldn't be determined (like because there was no tags in the template, or it was a plain text template), this * returns whatever the default is in the current configuration, so it's maybe * {@link Configuration#AUTO_DETECT_TAG_SYNTAX}. * * @see Configuration#setTagSyntax(int) * * @since 2.3.20 */
public int getActualTagSyntax() { return actualTagSyntax; }
Returns the interpolation syntax the parser has used for this template. Because the interpolation syntax is never auto-detected, it's not called "getActualInterpolationSyntax" (unlike getActualTagSyntax()).
See Also:
Returns:A constant like Configuration.LEGACY_INTERPOLATION_SYNTAX, Configuration.DOLLAR_INTERPOLATION_SYNTAX, or Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX.
Since:2.3.28
/** * Returns the interpolation syntax the parser has used for this template. Because the interpolation syntax is * never auto-detected, it's not called "getActualInterpolationSyntax" (unlike {@link #getActualTagSyntax()}). * * @return A constant like {@link Configuration#LEGACY_INTERPOLATION_SYNTAX}, * {@link Configuration#DOLLAR_INTERPOLATION_SYNTAX}, or * {@link Configuration#SQUARE_BRACKET_INTERPOLATION_SYNTAX}. * * @see Configuration#setInterpolationSyntax(int) * * @since 2.3.28 */
public int getInterpolationSyntax() { return interpolationSyntax; }
Returns the naming convention the parser has chosen for this template. If it could be determined, it's Configuration.LEGACY_NAMING_CONVENTION or Configuration.CAMEL_CASE_NAMING_CONVENTION. If it couldn't be determined (like because there no identifier that's part of the template language was used where the naming convention matters), this returns whatever the default is in the current configuration, so it's maybe Configuration.AUTO_DETECT_TAG_SYNTAX.
See Also:
Since:2.3.23
/** * Returns the naming convention the parser has chosen for this template. If it could be determined, it's * {@link Configuration#LEGACY_NAMING_CONVENTION} or {@link Configuration#CAMEL_CASE_NAMING_CONVENTION}. If it * couldn't be determined (like because there no identifier that's part of the template language was used where * the naming convention matters), this returns whatever the default is in the current configuration, so it's maybe * {@link Configuration#AUTO_DETECT_TAG_SYNTAX}. * * @see Configuration#setNamingConvention(int) * * @since 2.3.23 */
public int getActualNamingConvention() { return actualNamingConvention; }
Returns the output format (see Configuration.setOutputFormat(OutputFormat)) used for this template. The output format of a template can come from various places, in order of increasing priority: Configuration.getOutputFormat(), ParserConfiguration.getOutputFormat() (which is usually provided by Configuration.getTemplateConfigurations()) and the #ftl header's output_format option in the template.
Since:2.3.24
/** * Returns the output format (see {@link Configuration#setOutputFormat(OutputFormat)}) used for this template. * The output format of a template can come from various places, in order of increasing priority: * {@link Configuration#getOutputFormat()}, {@link ParserConfiguration#getOutputFormat()} (which is usually * provided by {@link Configuration#getTemplateConfigurations()}) and the {@code #ftl} header's {@code output_format} * option in the template. * * @since 2.3.24 */
public OutputFormat getOutputFormat() { return outputFormat; }
Meant to be called by the parser only.
/** * Meant to be called by the parser only. */
void setOutputFormat(OutputFormat outputFormat) { this.outputFormat = outputFormat; }
Returns if the template actually uses auto-escaping (see Configuration.setAutoEscapingPolicy(int)). This value is decided by the parser based on the actual OutputFormat, and the auto-escaping enums, in order of increasing priority: Configuration.getAutoEscapingPolicy(), ParserConfiguration.getAutoEscapingPolicy() (which is usually provided by Configuration.getTemplateConfigurations()), and finally on the #ftl header's auto_esc option in the template.
Since:2.3.24
/** * Returns if the template actually uses auto-escaping (see {@link Configuration#setAutoEscapingPolicy(int)}). This value * is decided by the parser based on the actual {@link OutputFormat}, and the auto-escaping enums, in order of * increasing priority: {@link Configuration#getAutoEscapingPolicy()}, {@link ParserConfiguration#getAutoEscapingPolicy()} * (which is usually provided by {@link Configuration#getTemplateConfigurations()}), and finally on the {@code #ftl} * header's {@code auto_esc} option in the template. * * @since 2.3.24 */
public boolean getAutoEscaping() { return autoEscaping; }
Meant to be called by the parser only.
/** * Meant to be called by the parser only. */
void setAutoEscaping(boolean autoEscaping) { this.autoEscaping = autoEscaping; }
Dump the raw template in canonical form.
/** * Dump the raw template in canonical form. */
public void dump(PrintStream ps) { ps.print(rootElement.getCanonicalForm()); }
Dump the raw template in canonical form.
/** * Dump the raw template in canonical form. */
public void dump(Writer out) throws IOException { out.write(rootElement.getCanonicalForm()); }
Called by code internally to maintain a table of macros
Deprecated:Should only be used internally, and might will be removed later.
/** * Called by code internally to maintain a table of macros * * @deprecated Should only be used internally, and might will be removed later. */
@Deprecated public void addMacro(Macro macro) { macros.put(macro.getName(), macro); }
Called by code internally to maintain a list of imports
Deprecated:Should only be used internally, and might will be removed later.
/** * Called by code internally to maintain a list of imports * * @deprecated Should only be used internally, and might will be removed later. */
@Deprecated public void addImport(LibraryLoad ll) { imports.add(ll); }
Returns the template source at the location specified by the coordinates given, or null if unavailable. A strange legacy in the behavior of this method is that it replaces tab characters with spaces according the value of getParserConfiguration()/ParserConfiguration.getTabSize() (which usually comes from Configuration.getTabSize()), because tab characters move the column number with more than 1 in error messages. However, if you set the tab size to 1, this method leaves the tab characters as is.
Params:
  • beginColumn – the first column of the requested source, 1-based
  • beginLine – the first line of the requested source, 1-based
  • endColumn – the last column of the requested source, 1-based
  • endLine – the last line of the requested source, 1-based
See Also:
/** * Returns the template source at the location specified by the coordinates given, or {@code null} if unavailable. * A strange legacy in the behavior of this method is that it replaces tab characters with spaces according the * value of {@link Template#getParserConfiguration()}/{@link ParserConfiguration#getTabSize()} (which usually * comes from {@link Configuration#getTabSize()}), because tab characters move the column number with more than * 1 in error messages. However, if you set the tab size to 1, this method leaves the tab characters as is. * * @param beginColumn the first column of the requested source, 1-based * @param beginLine the first line of the requested source, 1-based * @param endColumn the last column of the requested source, 1-based * @param endLine the last line of the requested source, 1-based * * @see freemarker.core.TemplateObject#getSource() */
public String getSource(int beginColumn, int beginLine, int endColumn, int endLine) { if (beginLine < 1 || endLine < 1) return null; // dynamically ?eval-ed expressions has no source available // Our container is zero-based. --beginLine; --beginColumn; --endColumn; --endLine; StringBuilder buf = new StringBuilder(); for (int i = beginLine ; i <= endLine; i++) { if (i < lines.size()) { buf.append(lines.get(i)); } } int lastLineLength = lines.get(endLine).toString().length(); int trailingCharsToDelete = lastLineLength - endColumn - 1; buf.delete(0, beginColumn); buf.delete(buf.length() - trailingCharsToDelete, buf.length()); return buf.toString(); }
Reader that builds up the line table info for us, and also helps in working around JavaCC's exception suppression.
/** * Reader that builds up the line table info for us, and also helps in working around JavaCC's exception * suppression. */
private class LineTableBuilder extends FilterReader { private final int tabSize; private final StringBuilder lineBuf = new StringBuilder(); int lastChar; boolean closed;
Needed to work around JavaCC behavior where it silently treats any errors as EOF.
/** Needed to work around JavaCC behavior where it silently treats any errors as EOF. */
private Exception failure;
Params:
  • r – the character stream to wrap
/** * @param r the character stream to wrap */
LineTableBuilder(Reader r, ParserConfiguration parserConfiguration) { super(r); tabSize = parserConfiguration.getTabSize(); } public boolean hasFailure() { return failure != null; } public void throwFailure() throws IOException { if (failure != null) { if (failure instanceof IOException) { throw (IOException) failure; } if (failure instanceof RuntimeException) { throw (RuntimeException) failure; } throw new UndeclaredThrowableException(failure); } } @Override public int read() throws IOException { try { int c = in.read(); handleChar(c); return c; } catch (Exception e) { throw rememberException(e); } } private IOException rememberException(Exception e) throws IOException { // JavaCC used to read from the Reader after it was closed. So we must not treat that as a failure. if (!closed) { failure = e; } if (e instanceof IOException) { return (IOException) e; } if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new UndeclaredThrowableException(e); } @Override public int read(char cbuf[], int off, int len) throws IOException { try { int numchars = in.read(cbuf, off, len); for (int i = off; i < off + numchars; i++) { char c = cbuf[i]; handleChar(c); } return numchars; } catch (Exception e) { throw rememberException(e); } } @Override public void close() throws IOException { if (lineBuf.length() > 0) { lines.add(lineBuf.toString()); lineBuf.setLength(0); } super.close(); closed = true; } private void handleChar(int c) { if (c == '\n' || c == '\r') { if (lastChar == '\r' && c == '\n') { // CRLF under Windoze int lastIndex = lines.size() - 1; String lastLine = (String) lines.get(lastIndex); lines.set(lastIndex, lastLine + '\n'); } else { lineBuf.append((char) c); lines.add(lineBuf.toString()); lineBuf.setLength(0); } } else if (c == '\t' && tabSize != 1) { int numSpaces = tabSize - (lineBuf.length() % tabSize); for (int i = 0; i < numSpaces; i++) { lineBuf.append(' '); } } else { lineBuf.append((char) c); } lastChar = c; } }
Deprecated:Should only be used internally, and might will be removed later.
/** * @deprecated Should only be used internally, and might will be removed later. */
@Deprecated public TemplateElement getRootTreeNode() { return rootElement; }
Deprecated:Should only be used internally, and might will be removed later.
/** * @deprecated Should only be used internally, and might will be removed later. */
@Deprecated public Map getMacros() { return macros; }
Deprecated:Should only be used internally, and might will be removed later.
/** * @deprecated Should only be used internally, and might will be removed later. */
@Deprecated public List getImports() { return imports; }
This is used internally.
Deprecated:Should only be used internally, and might will be removed later.
/** * This is used internally. * * @deprecated Should only be used internally, and might will be removed later. */
@Deprecated public void addPrefixNSMapping(String prefix, String nsURI) { if (nsURI.length() == 0) { throw new IllegalArgumentException("Cannot map empty string URI"); } if (prefix.length() == 0) { throw new IllegalArgumentException("Cannot map empty string prefix"); } if (prefix.equals(NO_NS_PREFIX)) { throw new IllegalArgumentException("The prefix: " + prefix + " cannot be registered, it's reserved for special internal use."); } if (prefixToNamespaceURILookup.containsKey(prefix)) { throw new IllegalArgumentException("The prefix: '" + prefix + "' was repeated. This is illegal."); } if (namespaceURIToPrefixLookup.containsKey(nsURI)) { throw new IllegalArgumentException("The namespace URI: " + nsURI + " cannot be mapped to 2 different prefixes."); } if (prefix.equals(DEFAULT_NAMESPACE_PREFIX)) { this.defaultNS = nsURI; } else { prefixToNamespaceURILookup.put(prefix, nsURI); namespaceURIToPrefixLookup.put(nsURI, prefix); } } public String getDefaultNS() { return this.defaultNS; }
Returns:the NamespaceUri mapped to this prefix in this template. (Or null if there is none.)
/** * @return the NamespaceUri mapped to this prefix in this template. (Or null if there is none.) */
public String getNamespaceForPrefix(String prefix) { if (prefix.equals("")) { return defaultNS == null ? "" : defaultNS; } return (String) prefixToNamespaceURILookup.get(prefix); }
Returns:the prefix mapped to this nsURI in this template. (Or null if there is none.)
/** * @return the prefix mapped to this nsURI in this template. (Or null if there is none.) */
public String getPrefixForNamespace(String nsURI) { if (nsURI == null) { return null; } if (nsURI.length() == 0) { return defaultNS == null ? "" : NO_NS_PREFIX; } if (nsURI.equals(defaultNS)) { return ""; } return (String) namespaceURIToPrefixLookup.get(nsURI); }
Returns:the prefixed name, based on the ns_prefixes defined in this template's header for the local name and node namespace passed in as parameters.
/** * @return the prefixed name, based on the ns_prefixes defined * in this template's header for the local name and node namespace * passed in as parameters. */
public String getPrefixedName(String localName, String nsURI) { if (nsURI == null || nsURI.length() == 0) { if (defaultNS != null) { return NO_NS_PREFIX + ":" + localName; } else { return localName; } } if (nsURI.equals(defaultNS)) { return localName; } String prefix = getPrefixForNamespace(nsURI); if (prefix == null) { return null; } return prefix + ":" + localName; }
Returns:an array of the TemplateElements containing the given column and line numbers.
Deprecated:Should only be used internally, and might will be removed later.
/** * @return an array of the {@link TemplateElement}s containing the given column and line numbers. * @deprecated Should only be used internally, and might will be removed later. */
@Deprecated public TreePath containingElements(int column, int line) { final ArrayList elements = new ArrayList(); TemplateElement element = rootElement; mainloop: while (element.contains(column, line)) { elements.add(element); for (Enumeration enumeration = element.children(); enumeration.hasMoreElements(); ) { TemplateElement elem = (TemplateElement) enumeration.nextElement(); if (elem.contains(column, line)) { element = elem; continue mainloop; } } break; } if (elements.isEmpty()) { return null; } return new TreePath(elements.toArray()); }
Thrown by the Template constructors that specify a non-null encoding whoch doesn't match the encoding specified in the #ftl header of the template.
/** * Thrown by the {@link Template} constructors that specify a non-{@code null} encoding whoch doesn't match the * encoding specified in the {@code #ftl} header of the template. */
static public class WrongEncodingException extends ParseException { private static final long serialVersionUID = 1L;
Deprecated:Use getTemplateSpecifiedEncoding() instead.
/** @deprecated Use {@link #getTemplateSpecifiedEncoding()} instead. */
@Deprecated public String specifiedEncoding; private final String constructorSpecifiedEncoding;
Deprecated:Use WrongEncodingException(String, String).
/** * @deprecated Use {@link #WrongEncodingException(String, String)}. */
@Deprecated public WrongEncodingException(String templateSpecifiedEncoding) { this(templateSpecifiedEncoding, null); }
Since:2.3.22
/** * @since 2.3.22 */
public WrongEncodingException(String templateSpecifiedEncoding, String constructorSpecifiedEncoding) { this.specifiedEncoding = templateSpecifiedEncoding; this.constructorSpecifiedEncoding = constructorSpecifiedEncoding; } @Override public String getMessage() { return "Encoding specified inside the template (" + specifiedEncoding + ") doesn't match the encoding specified for the Template constructor" + (constructorSpecifiedEncoding != null ? " (" + constructorSpecifiedEncoding + ")." : "."); }
Since:2.3.22
/** * @since 2.3.22 */
public String getTemplateSpecifiedEncoding() { return specifiedEncoding; }
Since:2.3.22
/** * @since 2.3.22 */
public String getConstructorSpecifiedEncoding() { return constructorSpecifiedEncoding; } } }