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

import java.io.Reader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import freemarker.cache.TemplateCache;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.Version;
import freemarker.template._TemplateAPI;
import freemarker.template.utility.NullArgumentException;

Used for customizing the configuration settings for individual Template-s (or rather groups of templates), relatively to the common setting values coming from the Configuration. This was designed with the standard template loading mechanism of FreeMarker in mind (Configuration.getTemplate(String) and TemplateCache), though can also be reused for custom template loading and caching solutions.

Note on the locale setting: When used with the standard template loading/caching mechanism ( Configuration.getTemplate(String) and its overloads), localized lookup happens before the locale specified here could have effect. The locale will be only set in the template that the localized lookup has already found.

Note on the encoding setting encoding: See setEncoding(String).

Note that the result value of the reader methods (getter and "is" methods) is usually not useful unless the value of that setting was already set on this object. Otherwise you will get the value from the parent Configuration, or an IllegalStateException before this object is associated to a Configuration.

If you are using this class for your own template loading and caching solution, rather than with the standard one, you should be aware of a few more details:

See Also:
Since:2.3.24
/** * Used for customizing the configuration settings for individual {@link Template}-s (or rather groups of templates), * relatively to the common setting values coming from the {@link Configuration}. This was designed with the standard * template loading mechanism of FreeMarker in mind ({@link Configuration#getTemplate(String)} * and {@link TemplateCache}), though can also be reused for custom template loading and caching solutions. * * <p> * Note on the {@code locale} setting: When used with the standard template loading/caching mechanism ( * {@link Configuration#getTemplate(String)} and its overloads), localized lookup happens before the {@code locale} * specified here could have effect. The {@code locale} will be only set in the template that the localized lookup has * already found. * * <p> * Note on the encoding setting {@code encoding}: See {@link #setEncoding(String)}. * * <p> * Note that the result value of the reader methods (getter and "is" methods) is usually not useful unless the value of * that setting was already set on this object. Otherwise you will get the value from the parent {@link Configuration}, * or an {@link IllegalStateException} before this object is associated to a {@link Configuration}. * * <p> * If you are using this class for your own template loading and caching solution, rather than with the standard one, * you should be aware of a few more details: * * <ul> * <li>This class implements both {@link Configurable} and {@link ParserConfiguration}. This means that it can influence * both the template parsing phase and the runtime settings. For both aspects (i.e., {@link ParserConfiguration} and * {@link Configurable}) to take effect, you have first pass this object to the {@link Template} constructor * (this is where the {@link ParserConfiguration} interface is used), and then you have to call {@link #apply(Template)} * on the resulting {@link Template} object (this is where the {@link Configurable} aspect is used). * * <li>{@link #apply(Template)} only change the settings that weren't yet set on the {@link Template} (but are inherited * from the {@link Configuration}). This is primarily because if the template configures itself via the {@code #ftl} * header, those values should have precedence. A consequence of this is that if you want to configure the same * {@link Template} with multiple {@link TemplateConfiguration}-s, you either should merge them to a single one before * that (with {@link #merge(TemplateConfiguration)}), or you have to apply them in reverse order of their intended * precedence. * </ul> * * @see Template#Template(String, String, Reader, Configuration, ParserConfiguration, String) * * @since 2.3.24 */
public final class TemplateConfiguration extends Configurable implements ParserConfiguration { private boolean parentConfigurationSet; private Integer tagSyntax; private Integer interpolationSyntax; private Integer namingConvention; private Boolean whitespaceStripping; private Boolean strictSyntaxMode; private Integer autoEscapingPolicy; private Boolean recognizeStandardFileExtensions; private OutputFormat outputFormat; private String encoding; private Integer tabSize;
Creates a new instance. The parent will be Configuration.getDefaultConfiguration() initially, but it will be changed to the real parent Configuration when this object is added to the Configuration. (It's not allowed to add the same instance to multiple Configuration-s).
/** * Creates a new instance. The parent will be {@link Configuration#getDefaultConfiguration()} initially, but it will * be changed to the real parent {@link Configuration} when this object is added to the {@link Configuration}. (It's * not allowed to add the same instance to multiple {@link Configuration}-s). */
public TemplateConfiguration() { super(Configuration.getDefaultConfiguration()); } /** * Same as {@link #setParentConfiguration(Configuration)}. */ @Override void setParent(Configurable cfg) { NullArgumentException.check("cfg", cfg); if (!(cfg instanceof Configuration)) { throw new IllegalArgumentException("The parent of a TemplateConfiguration can only be a Configuration"); } if (parentConfigurationSet) { if (getParent() != cfg) { throw new IllegalStateException( "This TemplateConfiguration is already associated with a different Configuration instance."); } return; } if (((Configuration) cfg).getIncompatibleImprovements().intValue() < _TemplateAPI.VERSION_INT_2_3_22 && hasAnyConfigurableSet()) { throw new IllegalStateException( "This TemplateConfiguration can't be associated to a Configuration that has " + "incompatibleImprovements less than 2.3.22, because it changes non-parser settings."); } super.setParent(cfg); parentConfigurationSet = true; }
Associates this instance with a Configuration; usually you don't call this, as it's called internally when this instance is added to a Configuration. This method can be called only once (except with the same Configuration parameter again, as that changes nothing anyway).
Throws:
/** * Associates this instance with a {@link Configuration}; usually you don't call this, as it's called internally * when this instance is added to a {@link Configuration}. This method can be called only once (except with the same * {@link Configuration} parameter again, as that changes nothing anyway). * * @throws IllegalArgumentException * if the argument is {@code null} or not a {@link Configuration} * @throws IllegalStateException * if this object is already associated to a different {@link Configuration} object, * or if the {@code Configuration} has {@code #getIncompatibleImprovements()} less than 2.3.22 and * this object tries to change any non-parser settings */
public void setParentConfiguration(Configuration cfg) { setParent(cfg); }
Returns the parent Configuration, or null if none was associated yet.
/** * Returns the parent {@link Configuration}, or {@code null} if none was associated yet. */
public Configuration getParentConfiguration() { return parentConfigurationSet ? (Configuration) getParent() : null; } private Configuration getNonNullParentConfiguration() { checkParentConfigurationSet(); return (Configuration) getParent(); }
Set all settings in this TemplateConfiguration that were set in the parameter TemplateConfiguration, possibly overwriting the earlier value in this object. (A setting is said to be set in a TemplateConfiguration if it was explicitly set via a setter method, as opposed to be inherited.)
/** * Set all settings in this {@link TemplateConfiguration} that were set in the parameter * {@link TemplateConfiguration}, possibly overwriting the earlier value in this object. (A setting is said to be * set in a {@link TemplateConfiguration} if it was explicitly set via a setter method, as opposed to be inherited.) */
public void merge(TemplateConfiguration tc) { if (tc.isAPIBuiltinEnabledSet()) { setAPIBuiltinEnabled(tc.isAPIBuiltinEnabled()); } if (tc.isArithmeticEngineSet()) { setArithmeticEngine(tc.getArithmeticEngine()); } if (tc.isAutoEscapingPolicySet()) { setAutoEscapingPolicy(tc.getAutoEscapingPolicy()); } if (tc.isAutoFlushSet()) { setAutoFlush(tc.getAutoFlush()); } if (tc.isBooleanFormatSet()) { setBooleanFormat(tc.getBooleanFormat()); } if (tc.isClassicCompatibleSet()) { setClassicCompatibleAsInt(tc.getClassicCompatibleAsInt()); } if (tc.isCustomDateFormatsSet()) { setCustomDateFormats(mergeMaps(getCustomDateFormats(), tc.getCustomDateFormats(), false)); } if (tc.isCustomNumberFormatsSet()) { setCustomNumberFormats(mergeMaps(getCustomNumberFormats(), tc.getCustomNumberFormats(), false)); } if (tc.isDateFormatSet()) { setDateFormat(tc.getDateFormat()); } if (tc.isDateTimeFormatSet()) { setDateTimeFormat(tc.getDateTimeFormat()); } if (tc.isEncodingSet()) { setEncoding(tc.getEncoding()); } if (tc.isLocaleSet()) { setLocale(tc.getLocale()); } if (tc.isLogTemplateExceptionsSet()) { setLogTemplateExceptions(tc.getLogTemplateExceptions()); } if (tc.isWrapUncheckedExceptionsSet()) { setWrapUncheckedExceptions(tc.getWrapUncheckedExceptions()); } if (tc.isNamingConventionSet()) { setNamingConvention(tc.getNamingConvention()); } if (tc.isNewBuiltinClassResolverSet()) { setNewBuiltinClassResolver(tc.getNewBuiltinClassResolver()); } if (tc.isNumberFormatSet()) { setNumberFormat(tc.getNumberFormat()); } if (tc.isObjectWrapperSet()) { setObjectWrapper(tc.getObjectWrapper()); } if (tc.isOutputEncodingSet()) { setOutputEncoding(tc.getOutputEncoding()); } if (tc.isOutputFormatSet()) { setOutputFormat(tc.getOutputFormat()); } if (tc.isRecognizeStandardFileExtensionsSet()) { setRecognizeStandardFileExtensions(tc.getRecognizeStandardFileExtensions()); } if (tc.isShowErrorTipsSet()) { setShowErrorTips(tc.getShowErrorTips()); } if (tc.isSQLDateAndTimeTimeZoneSet()) { setSQLDateAndTimeTimeZone(tc.getSQLDateAndTimeTimeZone()); } if (tc.isStrictSyntaxModeSet()) { setStrictSyntaxMode(tc.getStrictSyntaxMode()); } if (tc.isTagSyntaxSet()) { setTagSyntax(tc.getTagSyntax()); } if (tc.isInterpolationSyntaxSet()) { setInterpolationSyntax(tc.getInterpolationSyntax()); } if (tc.isTemplateExceptionHandlerSet()) { setTemplateExceptionHandler(tc.getTemplateExceptionHandler()); } if (tc.isAttemptExceptionReporterSet()) { setAttemptExceptionReporter(tc.getAttemptExceptionReporter()); } if (tc.isTimeFormatSet()) { setTimeFormat(tc.getTimeFormat()); } if (tc.isTimeZoneSet()) { setTimeZone(tc.getTimeZone()); } if (tc.isURLEscapingCharsetSet()) { setURLEscapingCharset(tc.getURLEscapingCharset()); } if (tc.isWhitespaceStrippingSet()) { setWhitespaceStripping(tc.getWhitespaceStripping()); } if (tc.isTabSizeSet()) { setTabSize(tc.getTabSize()); } if (tc.isLazyImportsSet()) { setLazyImports(tc.getLazyImports()); } if (tc.isLazyAutoImportsSet()) { setLazyAutoImports(tc.getLazyAutoImports()); } if (tc.isAutoImportsSet()) { setAutoImports(mergeMaps(getAutoImportsWithoutFallback(), tc.getAutoImportsWithoutFallback(), true)); } if (tc.isAutoIncludesSet()) { setAutoIncludes(mergeLists(getAutoIncludesWithoutFallback(), tc.getAutoIncludesWithoutFallback())); } tc.copyDirectCustomAttributes(this, true); }
Sets those settings of the Template which aren't yet set in the Template and are set in this TemplateConfiguration, leaves the other settings as is. A setting is said to be set in a TemplateConfiguration or Template if it was explicitly set via a setter method on that object, as opposed to be inherited from the Configuration.

Note that this method doesn't deal with settings that influence the parser, as those are already baked in at this point via the ParserConfiguration.

Note that the encoding setting of the Template counts as unset if it's null, even if null was set via Template.setEncoding(String).

Throws:
/** * Sets those settings of the {@link Template} which aren't yet set in the {@link Template} and are set in this * {@link TemplateConfiguration}, leaves the other settings as is. A setting is said to be set in a * {@link TemplateConfiguration} or {@link Template} if it was explicitly set via a setter method on that object, as * opposed to be inherited from the {@link Configuration}. * * <p> * Note that this method doesn't deal with settings that influence the parser, as those are already baked in at this * point via the {@link ParserConfiguration}. * * <p> * Note that the {@code encoding} setting of the {@link Template} counts as unset if it's {@code null}, * even if {@code null} was set via {@link Template#setEncoding(String)}. * * @throws IllegalStateException * If the parent configuration wasn't yet set. */
public void apply(Template template) { Configuration cfg = getNonNullParentConfiguration(); if (template.getConfiguration() != cfg) { // This is actually not a problem right now, but for future BC we enforce this. throw new IllegalArgumentException( "The argument Template doesn't belong to the same Configuration as the TemplateConfiguration"); } if (isAPIBuiltinEnabledSet() && !template.isAPIBuiltinEnabledSet()) { template.setAPIBuiltinEnabled(isAPIBuiltinEnabled()); } if (isArithmeticEngineSet() && !template.isArithmeticEngineSet()) { template.setArithmeticEngine(getArithmeticEngine()); } if (isAutoFlushSet() && !template.isAutoFlushSet()) { template.setAutoFlush(getAutoFlush()); } if (isBooleanFormatSet() && !template.isBooleanFormatSet()) { template.setBooleanFormat(getBooleanFormat()); } if (isClassicCompatibleSet() && !template.isClassicCompatibleSet()) { template.setClassicCompatibleAsInt(getClassicCompatibleAsInt()); } if (isCustomDateFormatsSet()) { template.setCustomDateFormats( mergeMaps(getCustomDateFormats(), template.getCustomDateFormatsWithoutFallback(), false)); } if (isCustomNumberFormatsSet()) { template.setCustomNumberFormats( mergeMaps(getCustomNumberFormats(), template.getCustomNumberFormatsWithoutFallback(), false)); } if (isDateFormatSet() && !template.isDateFormatSet()) { template.setDateFormat(getDateFormat()); } if (isDateTimeFormatSet() && !template.isDateTimeFormatSet()) { template.setDateTimeFormat(getDateTimeFormat()); } if (isEncodingSet() && template.getEncoding() == null) { template.setEncoding(getEncoding()); } if (isLocaleSet() && !template.isLocaleSet()) { template.setLocale(getLocale()); } if (isLogTemplateExceptionsSet() && !template.isLogTemplateExceptionsSet()) { template.setLogTemplateExceptions(getLogTemplateExceptions()); } if (isWrapUncheckedExceptionsSet() && !template.isWrapUncheckedExceptionsSet()) { template.setWrapUncheckedExceptions(getWrapUncheckedExceptions()); } if (isNewBuiltinClassResolverSet() && !template.isNewBuiltinClassResolverSet()) { template.setNewBuiltinClassResolver(getNewBuiltinClassResolver()); } if (isNumberFormatSet() && !template.isNumberFormatSet()) { template.setNumberFormat(getNumberFormat()); } if (isObjectWrapperSet() && !template.isObjectWrapperSet()) { template.setObjectWrapper(getObjectWrapper()); } if (isOutputEncodingSet() && !template.isOutputEncodingSet()) { template.setOutputEncoding(getOutputEncoding()); } if (isShowErrorTipsSet() && !template.isShowErrorTipsSet()) { template.setShowErrorTips(getShowErrorTips()); } if (isSQLDateAndTimeTimeZoneSet() && !template.isSQLDateAndTimeTimeZoneSet()) { template.setSQLDateAndTimeTimeZone(getSQLDateAndTimeTimeZone()); } if (isTemplateExceptionHandlerSet() && !template.isTemplateExceptionHandlerSet()) { template.setTemplateExceptionHandler(getTemplateExceptionHandler()); } if (isAttemptExceptionReporterSet() && !template.isAttemptExceptionReporterSet()) { template.setAttemptExceptionReporter(getAttemptExceptionReporter()); } if (isTimeFormatSet() && !template.isTimeFormatSet()) { template.setTimeFormat(getTimeFormat()); } if (isTimeZoneSet() && !template.isTimeZoneSet()) { template.setTimeZone(getTimeZone()); } if (isURLEscapingCharsetSet() && !template.isURLEscapingCharsetSet()) { template.setURLEscapingCharset(getURLEscapingCharset()); } if (isLazyImportsSet() && !template.isLazyImportsSet()) { template.setLazyImports(getLazyImports()); } if (isLazyAutoImportsSet() && !template.isLazyAutoImportsSet()) { template.setLazyAutoImports(getLazyAutoImports()); } if (isAutoImportsSet()) { // Regarding the order of the maps in the merge: // - Existing template-level imports have precedence over those coming from the TC (just as with the others // apply()-ed settings), thus for clashing import prefixes they must win. // - Template-level imports count as more specific, and so come after the more generic ones from TC. template.setAutoImports(mergeMaps(getAutoImports(), template.getAutoImportsWithoutFallback(), true)); } if (isAutoIncludesSet()) { template.setAutoIncludes(mergeLists(getAutoIncludes(), template.getAutoIncludesWithoutFallback())); } copyDirectCustomAttributes(template, false); } /** * See {@link Configuration#setTagSyntax(int)}. */ public void setTagSyntax(int tagSyntax) { _TemplateAPI.valideTagSyntaxValue(tagSyntax); this.tagSyntax = Integer.valueOf(tagSyntax); }
The getter pair of setTagSyntax(int).
/** * The getter pair of {@link #setTagSyntax(int)}. */
public int getTagSyntax() { return tagSyntax != null ? tagSyntax.intValue() : getNonNullParentConfiguration().getTagSyntax(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. */
public boolean isTagSyntaxSet() { return tagSyntax != null; } /** * See {@link Configuration#setInterpolationSyntax(int)}. */ public void setInterpolationSyntax(int interpolationSyntax) { _TemplateAPI.valideInterpolationSyntaxValue(interpolationSyntax); this.interpolationSyntax = Integer.valueOf(interpolationSyntax); }
The getter pair of setInterpolationSyntax(int).
/** * The getter pair of {@link #setInterpolationSyntax(int)}. */
public int getInterpolationSyntax() { return interpolationSyntax != null ? interpolationSyntax.intValue() : getNonNullParentConfiguration().getInterpolationSyntax(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. */
public boolean isInterpolationSyntaxSet() { return interpolationSyntax != null; } /** * See {@link Configuration#setNamingConvention(int)}. */ public void setNamingConvention(int namingConvention) { _TemplateAPI.validateNamingConventionValue(namingConvention); this.namingConvention = Integer.valueOf(namingConvention); }
The getter pair of setNamingConvention(int).
/** * The getter pair of {@link #setNamingConvention(int)}. */
public int getNamingConvention() { return namingConvention != null ? namingConvention.intValue() : getNonNullParentConfiguration().getNamingConvention(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. */
public boolean isNamingConventionSet() { return namingConvention != null; } /** * See {@link Configuration#setWhitespaceStripping(boolean)}. */ public void setWhitespaceStripping(boolean whitespaceStripping) { this.whitespaceStripping = Boolean.valueOf(whitespaceStripping); }
The getter pair of getWhitespaceStripping().
/** * The getter pair of {@link #getWhitespaceStripping()}. */
public boolean getWhitespaceStripping() { return whitespaceStripping != null ? whitespaceStripping.booleanValue() : getNonNullParentConfiguration().getWhitespaceStripping(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. */
public boolean isWhitespaceStrippingSet() { return whitespaceStripping != null; }
Sets the output format of the template; see Configuration.setAutoEscapingPolicy(int) for more.
/** * Sets the output format of the template; see {@link Configuration#setAutoEscapingPolicy(int)} for more. */
public void setAutoEscapingPolicy(int autoEscapingPolicy) { _TemplateAPI.validateAutoEscapingPolicyValue(autoEscapingPolicy); this.autoEscapingPolicy = Integer.valueOf(autoEscapingPolicy); }
The getter pair of setAutoEscapingPolicy(int).
/** * The getter pair of {@link #setAutoEscapingPolicy(int)}. */
public int getAutoEscapingPolicy() { return autoEscapingPolicy != null ? autoEscapingPolicy.intValue() : getNonNullParentConfiguration().getAutoEscapingPolicy(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. */
public boolean isAutoEscapingPolicySet() { return autoEscapingPolicy != null; }
Sets the output format of the template; see Configuration.setOutputFormat(OutputFormat) for more.
/** * Sets the output format of the template; see {@link Configuration#setOutputFormat(OutputFormat)} for more. */
public void setOutputFormat(OutputFormat outputFormat) { NullArgumentException.check("outputFormat", outputFormat); this.outputFormat = outputFormat; }
The getter pair of setOutputFormat(OutputFormat).
/** * The getter pair of {@link #setOutputFormat(OutputFormat)}. */
public OutputFormat getOutputFormat() { return outputFormat != null ? outputFormat : getNonNullParentConfiguration().getOutputFormat(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. */
public boolean isOutputFormatSet() { return outputFormat != null; } /** * See {@link Configuration#setRecognizeStandardFileExtensions(boolean)}. */ public void setRecognizeStandardFileExtensions(boolean recognizeStandardFileExtensions) { this.recognizeStandardFileExtensions = Boolean.valueOf(recognizeStandardFileExtensions); } /** * Getter pair of {@link #setRecognizeStandardFileExtensions(boolean)}. */ public boolean getRecognizeStandardFileExtensions() { return recognizeStandardFileExtensions != null ? recognizeStandardFileExtensions.booleanValue() : getNonNullParentConfiguration().getRecognizeStandardFileExtensions(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. */
public boolean isRecognizeStandardFileExtensionsSet() { return recognizeStandardFileExtensions != null; } /** * See {@link Configuration#setStrictSyntaxMode(boolean)}. */ public void setStrictSyntaxMode(boolean strictSyntaxMode) { this.strictSyntaxMode = Boolean.valueOf(strictSyntaxMode); }
The getter pair of setStrictSyntaxMode(boolean).
/** * The getter pair of {@link #setStrictSyntaxMode(boolean)}. */
public boolean getStrictSyntaxMode() { return strictSyntaxMode != null ? strictSyntaxMode.booleanValue() : getNonNullParentConfiguration().getStrictSyntaxMode(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. */
public boolean isStrictSyntaxModeSet() { return strictSyntaxMode != null; } @Override public void setStrictBeanModels(boolean strict) { throw new UnsupportedOperationException( "Setting strictBeanModels on " + TemplateConfiguration.class.getSimpleName() + " level isn't supported."); } public String getEncoding() { return encoding != null ? encoding : getNonNullParentConfiguration().getDefaultEncoding(); }
When the standard template loading/caching mechanism is used, this forces the charset used for reading the template "file", overriding everything but the encoding coming from the #ftl header. This setting overrides the locale-specific encodings set via Configuration.setEncoding(Locale, String). It also overrides the encoding parameter of Configuration.getTemplate(String, String) (and of its overloads) and the encoding parameter of the #include directive. This works like that because specifying the encoding where you are requesting the template is error prone and deprecated.

If you are developing your own template loading/caching mechanism instead of the standard one, note that the above behavior is not guaranteed by this class alone; you have to ensure it. Also, read the note on encoding in the documentation of apply(Template).

/** * When the standard template loading/caching mechanism is used, this forces the charset used for reading the * template "file", overriding everything but the encoding coming from the {@code #ftl} header. This setting * overrides the locale-specific encodings set via {@link Configuration#setEncoding(java.util.Locale, String)}. It * also overrides the {@code encoding} parameter of {@link Configuration#getTemplate(String, String)} (and of its * overloads) and the {@code encoding} parameter of the {@code #include} directive. This works like that because * specifying the encoding where you are requesting the template is error prone and deprecated. * * <p> * If you are developing your own template loading/caching mechanism instead of the standard one, note that the * above behavior is not guaranteed by this class alone; you have to ensure it. Also, read the note on * {@code encoding} in the documentation of {@link #apply(Template)}. */
public void setEncoding(String encoding) { NullArgumentException.check("encoding", encoding); this.encoding = encoding; } public boolean isEncodingSet() { return encoding != null; }
Since:2.3.25
/** * See {@link Configuration#setTabSize(int)}. * * @since 2.3.25 */
public void setTabSize(int tabSize) { this.tabSize = Integer.valueOf(tabSize); }
Getter pair of setTabSize(int).
Since:2.3.25
/** * Getter pair of {@link #setTabSize(int)}. * * @since 2.3.25 */
public int getTabSize() { return tabSize != null ? tabSize.intValue() : getNonNullParentConfiguration().getTabSize(); }
Tells if this setting is set directly in this object or its value is coming from the parent.
Since:2.3.25
/** * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}. * * @since 2.3.25 */
public boolean isTabSizeSet() { return tabSize != null; }
Returns Configuration.getIncompatibleImprovements() from the parent Configuration. This mostly just exist to satisfy the ParserConfiguration interface.
Throws:
/** * Returns {@link Configuration#getIncompatibleImprovements()} from the parent {@link Configuration}. This mostly * just exist to satisfy the {@link ParserConfiguration} interface. * * @throws IllegalStateException * If the parent configuration wasn't yet set. */
public Version getIncompatibleImprovements() { return getNonNullParentConfiguration().getIncompatibleImprovements(); } private void checkParentConfigurationSet() { if (!parentConfigurationSet) { throw new IllegalStateException("The TemplateConfiguration wasn't associated with a Configuration yet."); } } private boolean hasAnyConfigurableSet() { return isAPIBuiltinEnabledSet() || isArithmeticEngineSet() || isAutoFlushSet() || isAutoImportsSet() || isAutoIncludesSet() || isBooleanFormatSet() || isClassicCompatibleSet() || isCustomDateFormatsSet() || isCustomNumberFormatsSet() || isDateFormatSet() || isDateTimeFormatSet() || isLazyImportsSet() || isLazyAutoImportsSet() || isLocaleSet() || isLogTemplateExceptionsSet() || isWrapUncheckedExceptionsSet() || isNewBuiltinClassResolverSet() || isNumberFormatSet() || isObjectWrapperSet() || isOutputEncodingSet() || isShowErrorTipsSet() || isSQLDateAndTimeTimeZoneSet() || isTemplateExceptionHandlerSet() || isAttemptExceptionReporterSet() || isTimeFormatSet() || isTimeZoneSet() || isURLEscapingCharsetSet(); } private Map mergeMaps(Map m1, Map m2, boolean overwriteUpdatesOrder) { if (m1 == null) return m2; if (m2 == null) return m1; if (m1.isEmpty()) return m2; if (m2.isEmpty()) return m1; LinkedHashMap mergedM = new LinkedHashMap((m1.size() + m2.size()) * 4 / 3 + 1, 0.75f); mergedM.putAll(m1); for (Object m2Key : m2.keySet()) { mergedM.remove(m2Key); // So that duplicate keys are moved after m1 keys } mergedM.putAll(m2); return mergedM; } private List<String> mergeLists(List<String> list1, List<String> list2) { if (list1 == null) return list2; if (list2 == null) return list1; if (list1.isEmpty()) return list2; if (list2.isEmpty()) return list1; ArrayList<String> mergedList = new ArrayList<String>(list1.size() + list2.size()); mergedList.addAll(list1); mergedList.addAll(list2); return mergedList; } }