blob: 9bf0609d6f75c247f5a1cd19b74cc9cfa279ea75 [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.template;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import freemarker.cache.CacheStorageWithGetSize;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.NullCacheStorage;
import freemarker.cache.SoftCacheStorage;
import freemarker.cache.StringTemplateLoader;
import freemarker.cache.StrongCacheStorage;
import freemarker.cache.TemplateCache;
import freemarker.cache.TemplateLookupContext;
import freemarker.cache.TemplateLookupResult;
import freemarker.cache.TemplateLookupStrategy;
import freemarker.cache.TemplateNameFormat;
import freemarker.core.BaseNTemplateNumberFormatFactory;
import freemarker.core.CombinedMarkupOutputFormat;
import freemarker.core.Configurable;
import freemarker.core.Configurable.SettingValueAssignmentException;
import freemarker.core.Configurable.UnknownSettingException;
import freemarker.core.ConfigurableTest;
import freemarker.core.CustomHTMLOutputFormat;
import freemarker.core.DefaultTruncateBuiltinAlgorithm;
import freemarker.core.DummyOutputFormat;
import freemarker.core.Environment;
import freemarker.core.EpochMillisDivTemplateDateFormatFactory;
import freemarker.core.EpochMillisTemplateDateFormatFactory;
import freemarker.core.HTMLOutputFormat;
import freemarker.core.HexTemplateNumberFormatFactory;
import freemarker.core.MarkupOutputFormat;
import freemarker.core.OptInTemplateClassResolver;
import freemarker.core.OutputFormat;
import freemarker.core.ParseException;
import freemarker.core.RTFOutputFormat;
import freemarker.core.TemplateClassResolver;
import freemarker.core.TemplateDateFormatFactory;
import freemarker.core.TemplateNumberFormatFactory;
import freemarker.core.UndefinedOutputFormat;
import freemarker.core.UnregisteredOutputFormatException;
import freemarker.core.XHTMLOutputFormat;
import freemarker.core.XMLOutputFormat;
import freemarker.core._CoreStringUtils;
import freemarker.ext.beans.BeansWrapperBuilder;
import freemarker.ext.beans.LegacyDefaultMemberAccessPolicy;
import freemarker.ext.beans.MemberAccessPolicy;
import freemarker.ext.beans.MemberSelectorListMemberAccessPolicy;
import freemarker.ext.beans.StringModel;
import freemarker.ext.beans.WhitelistMemberAccessPolicy;
import freemarker.template.utility.DateUtil;
import freemarker.template.utility.NullArgumentException;
import freemarker.template.utility.NullWriter;
import junit.framework.TestCase;
public class ConfigurationTest extends TestCase {
public ConfigurationTest(String name) {
super(name);
}
public void testIncompatibleImprovementsChangesDefaults() {
Version newVersion = Configuration.VERSION_2_3_21;
Version oldVersion = Configuration.VERSION_2_3_20;
Configuration cfg = new Configuration();
assertUsesLegacyObjectWrapper(cfg);
assertUsesLegacyTemplateLoader(cfg);
assertEquals(cfg.getIncompatibleImprovements(), Configuration.VERSION_2_3_0);
cfg.setIncompatibleImprovements(newVersion);
assertUsesNewObjectWrapper(cfg);
assertUsesNewTemplateLoader(cfg);
cfg.setIncompatibleImprovements(oldVersion);
assertUsesLegacyObjectWrapper(cfg);
assertUsesLegacyTemplateLoader(cfg);
cfg.setIncompatibleImprovements(oldVersion);
assertUsesLegacyObjectWrapper(cfg);
assertUsesLegacyTemplateLoader(cfg);
cfg.setIncompatibleImprovements(newVersion);
assertUsesNewObjectWrapper(cfg);
assertUsesNewTemplateLoader(cfg);
cfg.setIncompatibleImprovements(newVersion);
assertUsesNewObjectWrapper(cfg);
assertUsesNewTemplateLoader(cfg);
cfg.setObjectWrapper(new SimpleObjectWrapper());
cfg.setIncompatibleImprovements(oldVersion);
assertSame(SimpleObjectWrapper.class, cfg.getObjectWrapper().getClass());
assertUsesLegacyTemplateLoader(cfg);
cfg.setTemplateLoader(new StringTemplateLoader());
cfg.setIncompatibleImprovements(newVersion);
assertSame(SimpleObjectWrapper.class, cfg.getObjectWrapper().getClass());
assertSame(StringTemplateLoader.class, cfg.getTemplateLoader().getClass());
cfg.setIncompatibleImprovements(oldVersion);
assertSame(SimpleObjectWrapper.class, cfg.getObjectWrapper().getClass());
assertSame(StringTemplateLoader.class, cfg.getTemplateLoader().getClass());
cfg.setObjectWrapper(ObjectWrapper.DEFAULT_WRAPPER);
cfg.setIncompatibleImprovements(newVersion);
assertSame(ObjectWrapper.DEFAULT_WRAPPER, cfg.getObjectWrapper());
assertSame(StringTemplateLoader.class, cfg.getTemplateLoader().getClass());
cfg.unsetObjectWrapper();
assertUsesNewObjectWrapper(cfg);
cfg.unsetTemplateLoader();
assertUsesNewTemplateLoader(cfg);
cfg.setIncompatibleImprovements(oldVersion);
assertUsesLegacyObjectWrapper(cfg);
assertUsesLegacyTemplateLoader(cfg);
cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_22);
assertUses2322ObjectWrapper(cfg);
assertUsesNewTemplateLoader(cfg);
// ---
cfg = new Configuration(newVersion);
assertUsesNewObjectWrapper(cfg);
assertUsesNewTemplateLoader(cfg);
cfg.setIncompatibleImprovements(oldVersion);
assertUsesLegacyObjectWrapper(cfg);
assertUsesLegacyTemplateLoader(cfg);
// ---
cfg = new Configuration(Configuration.VERSION_2_3_22);
assertUses2322ObjectWrapper(cfg);
assertUsesNewTemplateLoader(cfg);
cfg = new Configuration(Configuration.VERSION_2_3_25);
assertFalse(((DefaultObjectWrapper) cfg.getObjectWrapper()).getTreatDefaultMethodsAsBeanMembers());
cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_26);
assertTrue(((DefaultObjectWrapper) cfg.getObjectWrapper()).getTreatDefaultMethodsAsBeanMembers());
assertTrue(((DefaultObjectWrapper) cfg.getObjectWrapper()).getPreferIndexedReadMethod());
cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_27);
assertTrue(((DefaultObjectWrapper) cfg.getObjectWrapper()).getTreatDefaultMethodsAsBeanMembers());
assertFalse(((DefaultObjectWrapper) cfg.getObjectWrapper()).getPreferIndexedReadMethod());
}
private void assertUses2322ObjectWrapper(Configuration cfg) {
Object ow = cfg.getObjectWrapper();
assertEquals(DefaultObjectWrapper.class, ow.getClass());
assertEquals(Configuration.VERSION_2_3_22,
((DefaultObjectWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements());
}
public void testUnsetAndIsExplicitlySet() {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertFalse(cfg.isLogTemplateExceptionsExplicitlySet());
assertTrue(cfg.getLogTemplateExceptions());
//
cfg.setLogTemplateExceptions(false);
assertTrue(cfg.isLogTemplateExceptionsExplicitlySet());
assertFalse(cfg.getLogTemplateExceptions());
//
for (int i = 0; i < 2; i++) {
cfg.unsetLogTemplateExceptions();
assertFalse(cfg.isLogTemplateExceptionsExplicitlySet());
assertTrue(cfg.getLogTemplateExceptions());
}
assertFalse(cfg.isObjectWrapperExplicitlySet());
assertSame(ObjectWrapper.DEFAULT_WRAPPER, cfg.getObjectWrapper());
//
cfg.setObjectWrapper(ObjectWrapper.SIMPLE_WRAPPER);
assertTrue(cfg.isObjectWrapperExplicitlySet());
assertSame(ObjectWrapper.SIMPLE_WRAPPER, cfg.getObjectWrapper());
//
for (int i = 0; i < 2; i++) {
cfg.unsetObjectWrapper();
assertFalse(cfg.isObjectWrapperExplicitlySet());
assertSame(ObjectWrapper.DEFAULT_WRAPPER, cfg.getObjectWrapper());
}
assertFalse(cfg.isTemplateExceptionHandlerExplicitlySet());
assertSame(TemplateExceptionHandler.DEBUG_HANDLER, cfg.getTemplateExceptionHandler());
//
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
assertTrue(cfg.isTemplateExceptionHandlerExplicitlySet());
assertSame(TemplateExceptionHandler.RETHROW_HANDLER, cfg.getTemplateExceptionHandler());
//
for (int i = 0; i < 2; i++) {
cfg.unsetTemplateExceptionHandler();
assertFalse(cfg.isTemplateExceptionHandlerExplicitlySet());
assertSame(TemplateExceptionHandler.DEBUG_HANDLER, cfg.getTemplateExceptionHandler());
}
assertFalse(cfg.isTemplateLoaderExplicitlySet());
assertTrue(cfg.getTemplateLoader() instanceof FileTemplateLoader);
//
cfg.setTemplateLoader(null);
assertTrue(cfg.isTemplateLoaderExplicitlySet());
assertNull(cfg.getTemplateLoader());
//
for (int i = 0; i < 3; i++) {
if (i == 2) {
cfg.setTemplateLoader(cfg.getTemplateLoader());
}
cfg.unsetTemplateLoader();
assertFalse(cfg.isTemplateLoaderExplicitlySet());
assertTrue(cfg.getTemplateLoader() instanceof FileTemplateLoader);
}
assertFalse(cfg.isTemplateLookupStrategyExplicitlySet());
assertSame(TemplateLookupStrategy.DEFAULT_2_3_0, cfg.getTemplateLookupStrategy());
//
cfg.setTemplateLookupStrategy(TemplateLookupStrategy.DEFAULT_2_3_0);
assertTrue(cfg.isTemplateLookupStrategyExplicitlySet());
//
for (int i = 0; i < 2; i++) {
cfg.unsetTemplateLookupStrategy();
assertFalse(cfg.isTemplateLookupStrategyExplicitlySet());
}
assertFalse(cfg.isTemplateNameFormatExplicitlySet());
assertSame(TemplateNameFormat.DEFAULT_2_3_0, cfg.getTemplateNameFormat());
//
cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0);
assertTrue(cfg.isTemplateNameFormatExplicitlySet());
assertSame(TemplateNameFormat.DEFAULT_2_4_0, cfg.getTemplateNameFormat());
//
for (int i = 0; i < 2; i++) {
cfg.unsetTemplateNameFormat();
assertFalse(cfg.isTemplateNameFormatExplicitlySet());
assertSame(TemplateNameFormat.DEFAULT_2_3_0, cfg.getTemplateNameFormat());
}
assertFalse(cfg.isCacheStorageExplicitlySet());
assertTrue(cfg.getCacheStorage() instanceof SoftCacheStorage);
//
cfg.setCacheStorage(NullCacheStorage.INSTANCE);
assertTrue(cfg.isCacheStorageExplicitlySet());
assertSame(NullCacheStorage.INSTANCE, cfg.getCacheStorage());
//
for (int i = 0; i < 3; i++) {
if (i == 2) {
cfg.setCacheStorage(cfg.getCacheStorage());
}
cfg.unsetCacheStorage();
assertFalse(cfg.isCacheStorageExplicitlySet());
assertTrue(cfg.getCacheStorage() instanceof SoftCacheStorage);
}
}
public void testTemplateLoadingErrors() throws Exception {
Configuration cfg = new Configuration();
try {
cfg.getTemplate("missing.ftl");
fail();
} catch (TemplateNotFoundException e) {
assertThat(e.getMessage(), allOf(containsString("wasn't set"), containsString("default")));
}
cfg = new Configuration(Configuration.VERSION_2_3_21);
try {
cfg.getTemplate("missing.ftl");
fail();
} catch (TemplateNotFoundException e) {
assertThat(e.getMessage(), allOf(containsString("wasn't set"), not(containsString("default"))));
}
cfg.setClassForTemplateLoading(this.getClass(), "nosuchpackage");
try {
cfg.getTemplate("missing.ftl");
fail();
} catch (TemplateNotFoundException e) {
assertThat(e.getMessage(), not(containsString("wasn't set")));
}
}
private void assertUsesLegacyObjectWrapper(Configuration cfg) {
assertSame(ObjectWrapper.DEFAULT_WRAPPER, cfg.getObjectWrapper());
}
private void assertUsesNewObjectWrapper(Configuration cfg) {
assertEquals(
Configuration.VERSION_2_3_21,
((DefaultObjectWrapper) cfg.getObjectWrapper()).getIncompatibleImprovements());
}
private void assertUsesNewTemplateLoader(Configuration cfg) {
assertNull(cfg.getTemplateLoader());
}
private void assertUsesLegacyTemplateLoader(Configuration cfg) {
assertTrue(cfg.getTemplateLoader() instanceof FileTemplateLoader);
}
public void testVersion() {
Version v = Configuration.getVersion();
assertTrue(v.intValue() > _TemplateAPI.VERSION_INT_2_3_20);
assertSame(v.toString(), Configuration.getVersionNumber());
try {
new Configuration(new Version(999, 1, 2));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("upgrade"));
}
try {
new Configuration(new Version(2, 2, 2));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("2.3.0"));
}
}
public void testShowErrorTips() throws Exception {
Configuration cfg = new Configuration();
try {
new Template(null, "${x}", cfg).process(null, NullWriter.INSTANCE);
fail();
} catch (TemplateException e) {
assertThat(e.getMessage(), containsString("Tip:"));
}
cfg.setShowErrorTips(false);
try {
new Template(null, "${x}", cfg).process(null, NullWriter.INSTANCE);
fail();
} catch (TemplateException e) {
assertThat(e.getMessage(), not(containsString("Tip:")));
}
}
@Test
@SuppressWarnings("boxing")
public void testGetTemplateOverloads() throws IOException, TemplateException {
final Locale hu = new Locale("hu", "HU");
final String latin1 = "ISO-8859-1";
final String latin2 = "ISO-8859-2";
final String utf8 = "utf-8";
final String tFtl = "t.ftl";
final String tEnFtl = "t_en.ftl";
final String tUtf8Ftl = "t-utf8.ftl";
final Integer custLookupCond = 123;
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
cfg.setLocale(Locale.GERMAN);
cfg.setDefaultEncoding(latin1);
cfg.setEncoding(hu, latin2);
StringTemplateLoader tl = new StringTemplateLoader();
tl.putTemplate(tFtl, "${1}");
tl.putTemplate(tEnFtl, "${1}");
tl.putTemplate(tUtf8Ftl, "<#ftl encoding='utf-8'>");
cfg.setTemplateLoader(tl);
// 1 args:
{
Template t = cfg.getTemplate(tFtl);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin1, t.getEncoding());
}
{
Template t = cfg.getTemplate(tUtf8Ftl);
assertEquals(tUtf8Ftl, t.getName());
assertEquals(tUtf8Ftl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
}
// 2 args overload 1:
{
Template t = cfg.getTemplate(tFtl, Locale.GERMAN);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin1, t.getEncoding());
}
{
Template t = cfg.getTemplate(tFtl, (Locale) null);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin1, t.getEncoding());
}
{
Template t = cfg.getTemplate(tFtl, Locale.US);
assertEquals(tFtl, t.getName());
assertEquals(tEnFtl, t.getSourceName());
assertEquals(Locale.US, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin1, t.getEncoding());
}
{
Template t = cfg.getTemplate(tUtf8Ftl, Locale.US);
assertEquals(tUtf8Ftl, t.getName());
assertEquals(tUtf8Ftl, t.getSourceName());
assertEquals(Locale.US, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
}
{
Template t = cfg.getTemplate(tFtl, hu);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin2, t.getEncoding());
}
{
Template t = cfg.getTemplate(tUtf8Ftl, hu);
assertEquals(tUtf8Ftl, t.getName());
assertEquals(tUtf8Ftl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
}
// 2 args overload 2:
{
Template t = cfg.getTemplate(tFtl, utf8);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
}
{
Template t = cfg.getTemplate(tFtl, (String) null);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin1, t.getEncoding());
}
// 3 args:
{
Template t = cfg.getTemplate(tFtl, hu, utf8);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("1", t);
}
{
Template t = cfg.getTemplate(tFtl, hu, null);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin2, t.getEncoding());
assertOutputEquals("1", t);
}
{
Template t = cfg.getTemplate(tFtl, null, utf8);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("1", t);
}
{
Template t = cfg.getTemplate(tFtl, null, null);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin1, t.getEncoding());
assertOutputEquals("1", t);
}
// 4 args:
{
Template t = cfg.getTemplate(tFtl, hu, utf8, false);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("${1}", t);
}
{
Template t = cfg.getTemplate(tFtl, hu, utf8, true);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("1", t);
}
{
Template t = cfg.getTemplate(tFtl, null, utf8, true);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("1", t);
}
{
Template t = cfg.getTemplate(tFtl, hu, null, true);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin2, t.getEncoding());
assertOutputEquals("1", t);
}
// 5 args:
{
Template t = cfg.getTemplate(tFtl, hu, utf8, false, true);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("${1}", t);
}
{
Template t = cfg.getTemplate(tFtl, hu, utf8, true, false);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("1", t);
}
{
Template t = cfg.getTemplate(tFtl, null, utf8, true, false);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("1", t);
}
{
Template t = cfg.getTemplate(tFtl, hu, null, true, false);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertNull(t.getCustomLookupCondition());
assertEquals(latin2, t.getEncoding());
assertOutputEquals("1", t);
}
try {
cfg.getTemplate("missing.ftl", hu, utf8, true, false);
fail();
} catch (TemplateNotFoundException e) {
// Expected
}
assertNull(cfg.getTemplate("missing.ftl", hu, utf8, true, true));
// 6 args:
{
Template t = cfg.getTemplate(tFtl, hu, custLookupCond, utf8, true, false);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertEquals(custLookupCond, t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("1", t);
}
{
Template t = cfg.getTemplate(tFtl, hu, custLookupCond, utf8, false, false);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertEquals(custLookupCond, t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("${1}", t);
}
{
Template t = cfg.getTemplate(tFtl, null, custLookupCond, utf8, true, false);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(Locale.GERMAN, t.getLocale());
assertEquals(custLookupCond, t.getCustomLookupCondition());
assertEquals(utf8, t.getEncoding());
assertOutputEquals("1", t);
}
{
Template t = cfg.getTemplate(tFtl, hu, custLookupCond, null, true, false);
assertEquals(tFtl, t.getName());
assertEquals(tFtl, t.getSourceName());
assertEquals(hu, t.getLocale());
assertEquals(custLookupCond, t.getCustomLookupCondition());
assertEquals(latin2, t.getEncoding());
assertOutputEquals("1", t);
}
try {
cfg.getTemplate("missing.ftl", hu, custLookupCond, utf8, true, false);
fail();
} catch (TemplateNotFoundException e) {
// Expected
}
assertNull(cfg.getTemplate("missing.ftl", hu, custLookupCond, utf8, true, true));
}
private void assertOutputEquals(final String expectedContent, final Template t) throws TemplateException,
IOException {
StringWriter sw = new StringWriter();
t.process(null, sw);
assertEquals(expectedContent, sw.toString());
}
public void testSetTemplateLoaderAndCache() throws Exception {
Configuration cfg = new Configuration();
CacheStorageWithGetSize cacheStorage = (CacheStorageWithGetSize) cfg.getCacheStorage();
assertEquals(0, cacheStorage.getSize());
cfg.setCacheStorage(new StrongCacheStorage());
cacheStorage = (CacheStorageWithGetSize) cfg.getCacheStorage();
assertEquals(0, cacheStorage.getSize());
cfg.setClassForTemplateLoading(ConfigurationTest.class, "");
assertEquals(0, cacheStorage.getSize());
cfg.getTemplate("toCache1.ftl");
assertEquals(1, cacheStorage.getSize());
cfg.getTemplate("toCache2.ftl");
assertEquals(2, cacheStorage.getSize());
cfg.setClassForTemplateLoading(ConfigurationTest.class, "");
assertEquals(0, cacheStorage.getSize());
cfg.getTemplate("toCache1.ftl");
assertEquals(1, cacheStorage.getSize());
cfg.setTemplateLoader(cfg.getTemplateLoader());
assertEquals(1, cacheStorage.getSize());
}
public void testChangingLocalizedLookupClearsCache() throws Exception {
Configuration cfg = new Configuration();
cfg.setCacheStorage(new StrongCacheStorage());
CacheStorageWithGetSize cache = (CacheStorageWithGetSize) cfg.getCacheStorage();
cache = (CacheStorageWithGetSize) cfg.getCacheStorage();
assertEquals(0, cache.getSize());
cfg.setClassForTemplateLoading(ConfigurationTest.class, "");
assertEquals(0, cache.getSize());
cfg.getTemplate("toCache1.ftl");
assertEquals(1, cache.getSize());
cfg.setLocalizedLookup(true);
assertEquals(1, cache.getSize());
cfg.setLocalizedLookup(false);
assertEquals(0, cache.getSize());
cfg.getTemplate("toCache1.ftl");
assertEquals(1, cache.getSize());
cfg.setLocalizedLookup(false);
assertEquals(1, cache.getSize());
cfg.setLocalizedLookup(true);
assertEquals(0, cache.getSize());
}
public void testChangingTemplateNameFormatClearsCache() throws Exception {
Configuration cfg = new Configuration();
cfg.setCacheStorage(new StrongCacheStorage());
CacheStorageWithGetSize cache = (CacheStorageWithGetSize) cfg.getCacheStorage();
cache = (CacheStorageWithGetSize) cfg.getCacheStorage();
assertEquals(0, cache.getSize());
cfg.setClassForTemplateLoading(ConfigurationTest.class, "");
assertEquals(0, cache.getSize());
cfg.getTemplate("toCache1.ftl");
assertEquals(1, cache.getSize());
cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_3_0);
assertEquals(1, cache.getSize());
cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0);
assertEquals(0, cache.getSize());
cfg.getTemplate("toCache1.ftl");
assertEquals(1, cache.getSize());
cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0);
assertEquals(1, cache.getSize());
cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_3_0);
assertEquals(0, cache.getSize());
}
public void testChangingTemplateNameFormatHasEffect() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
StringTemplateLoader tl = new StringTemplateLoader();
tl.putTemplate("a/b.ftl", "In a/b.ftl");
tl.putTemplate("b.ftl", "In b.ftl");
cfg.setTemplateLoader(tl);
{
final Template template = cfg.getTemplate("a/./../b.ftl");
assertEquals("a/b.ftl", template.getName());
assertEquals("a/b.ftl", template.getSourceName());
assertEquals("In a/b.ftl", template.toString());
}
cfg.setTemplateNameFormat(TemplateNameFormat.DEFAULT_2_4_0);
{
final Template template = cfg.getTemplate("a/./../b.ftl");
assertEquals("b.ftl", template.getName());
assertEquals("b.ftl", template.getSourceName());
assertEquals("In b.ftl", template.toString());
}
}
public void testTemplateNameFormatSetSetting() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
assertSame(TemplateNameFormat.DEFAULT_2_3_0, cfg.getTemplateNameFormat());
cfg.setSetting(Configuration.TEMPLATE_NAME_FORMAT_KEY, "defAult_2_4_0");
assertSame(TemplateNameFormat.DEFAULT_2_4_0, cfg.getTemplateNameFormat());
cfg.setSetting(Configuration.TEMPLATE_NAME_FORMAT_KEY, "defaUlt_2_3_0");
assertSame(TemplateNameFormat.DEFAULT_2_3_0, cfg.getTemplateNameFormat());
assertTrue(cfg.isTemplateNameFormatExplicitlySet());
cfg.setSetting(Configuration.TEMPLATE_NAME_FORMAT_KEY, "defauLt");
assertFalse(cfg.isTemplateNameFormatExplicitlySet());
}
public void testObjectWrapperSetSetting() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_20);
{
cfg.setSetting(Configurable.OBJECT_WRAPPER_KEY, "defAult");
assertSame(ObjectWrapper.DEFAULT_WRAPPER, cfg.getObjectWrapper());
}
{
cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_22);
assertNotSame(ObjectWrapper.DEFAULT_WRAPPER, cfg.getObjectWrapper());
DefaultObjectWrapper dow = (DefaultObjectWrapper) cfg.getObjectWrapper();
assertEquals(Configuration.VERSION_2_3_22, dow.getIncompatibleImprovements());
assertTrue(dow.getForceLegacyNonListCollections());
}
{
cfg.setSetting(Configurable.OBJECT_WRAPPER_KEY, "defAult_2_3_0");
assertSame(ObjectWrapper.DEFAULT_WRAPPER, cfg.getObjectWrapper());
}
{
cfg.setSetting(Configurable.OBJECT_WRAPPER_KEY,
"DefaultObjectWrapper(2.3.21, useAdaptersForContainers=true, forceLegacyNonListCollections=false)");
DefaultObjectWrapper dow = (DefaultObjectWrapper) cfg.getObjectWrapper();
assertEquals(Configuration.VERSION_2_3_21, dow.getIncompatibleImprovements());
assertFalse(dow.getForceLegacyNonListCollections());
}
{
cfg.setSetting(Configurable.OBJECT_WRAPPER_KEY, "defAult");
DefaultObjectWrapper dow = (DefaultObjectWrapper) cfg.getObjectWrapper();
assertEquals(Configuration.VERSION_2_3_22, dow.getIncompatibleImprovements());
assertTrue(dow.getForceLegacyNonListCollections());
}
}
public void testTemplateLookupStrategyDefaultAndSet() throws IOException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
assertSame(TemplateLookupStrategy.DEFAULT_2_3_0, cfg.getTemplateLookupStrategy());
cfg.setClassForTemplateLoading(ConfigurationTest.class, "");
assertSame(TemplateLookupStrategy.DEFAULT_2_3_0, cfg.getTemplateLookupStrategy());
CacheStorageWithGetSize cache = (CacheStorageWithGetSize) cfg.getCacheStorage();
cfg.setClassForTemplateLoading(ConfigurationTest.class, "");
assertEquals(0, cache.getSize());
cfg.getTemplate("toCache1.ftl");
assertEquals(1, cache.getSize());
cfg.setTemplateLookupStrategy(TemplateLookupStrategy.DEFAULT_2_3_0);
assertEquals(1, cache.getSize());
final TemplateLookupStrategy myStrategy = new TemplateLookupStrategy() {
@Override
public TemplateLookupResult lookup(TemplateLookupContext ctx) throws IOException {
return ctx.lookupWithAcquisitionStrategy(ctx.getTemplateName());
}
};
cfg.setTemplateLookupStrategy(myStrategy);
assertEquals(0, cache.getSize());
assertSame(myStrategy, cfg.getTemplateLookupStrategy());
cfg.getTemplate("toCache1.ftl");
assertEquals(1, cache.getSize());
cfg.setTemplateLookupStrategy(myStrategy);
assertEquals(1, cache.getSize());
cfg.setTemplateLookupStrategy(TemplateLookupStrategy.DEFAULT_2_3_0);
assertEquals(0, cache.getSize());
}
public void testSetTemplateConfigurations() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
assertNull(cfg.getTemplateConfigurations());
StringTemplateLoader tl = new StringTemplateLoader();
tl.putTemplate("t.de.ftlh", "");
tl.putTemplate("t.fr.ftlx", "");
tl.putTemplate("t.ftlx", "");
tl.putTemplate("Stat/t.de.ftlx", "");
cfg.setTemplateLoader(tl);
cfg.setTimeZone(TimeZone.getTimeZone("GMT+09"));
cfg.setSetting(Configuration.TEMPLATE_CONFIGURATIONS_KEY,
"MergingTemplateConfigurationFactory("
+ "FirstMatchTemplateConfigurationFactory("
+ "ConditionalTemplateConfigurationFactory("
+ "FileNameGlobMatcher('*.de.*'), TemplateConfiguration(timeZone=TimeZone('GMT+01'))), "
+ "ConditionalTemplateConfigurationFactory("
+ "FileNameGlobMatcher('*.fr.*'), TemplateConfiguration(timeZone=TimeZone('GMT'))), "
+ "allowNoMatch=true"
+ "), "
+ "FirstMatchTemplateConfigurationFactory("
+ "ConditionalTemplateConfigurationFactory("
+ "FileExtensionMatcher('ftlh'), TemplateConfiguration(booleanFormat='TODO,HTML')), "
+ "ConditionalTemplateConfigurationFactory("
+ "FileExtensionMatcher('ftlx'), TemplateConfiguration(booleanFormat='TODO,XML')), "
+ "noMatchErrorDetails='Unrecognized template file extension'"
+ "), "
+ "ConditionalTemplateConfigurationFactory("
+ "PathGlobMatcher('stat/**', caseInsensitive=true), "
+ "TemplateConfiguration(timeZone=TimeZone('UTC'))"
+ ")"
+ ")");
{
Template t = cfg.getTemplate("t.de.ftlh");
assertEquals("TODO,HTML", t.getBooleanFormat());
assertEquals(TimeZone.getTimeZone("GMT+01"), t.getTimeZone());
}
{
Template t = cfg.getTemplate("t.fr.ftlx");
assertEquals("TODO,XML", t.getBooleanFormat());
assertEquals(TimeZone.getTimeZone("GMT"), t.getTimeZone());
}
{
Template t = cfg.getTemplate("t.ftlx");
assertEquals("TODO,XML", t.getBooleanFormat());
assertEquals(TimeZone.getTimeZone("GMT+09"), t.getTimeZone());
}
{
Template t = cfg.getTemplate("Stat/t.de.ftlx");
assertEquals("TODO,XML", t.getBooleanFormat());
assertEquals(DateUtil.UTC, t.getTimeZone());
}
assertNotNull(cfg.getTemplateConfigurations());
cfg.setSetting(Configuration.TEMPLATE_CONFIGURATIONS_KEY, "null");
assertNull(cfg.getTemplateConfigurations());
}
public void testSetAutoEscaping() throws Exception {
Configuration cfg = new Configuration();
assertEquals(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY, cfg.getAutoEscapingPolicy());
cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY);
assertEquals(Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY, cfg.getAutoEscapingPolicy());
cfg.setAutoEscapingPolicy(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY);
assertEquals(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY, cfg.getAutoEscapingPolicy());
cfg.setAutoEscapingPolicy(Configuration.DISABLE_AUTO_ESCAPING_POLICY);
assertEquals(Configuration.DISABLE_AUTO_ESCAPING_POLICY, cfg.getAutoEscapingPolicy());
cfg.setSetting(Configuration.AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE, "enableIfSupported");
assertEquals(Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY, cfg.getAutoEscapingPolicy());
cfg.setSetting(Configuration.AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE, "enable_if_supported");
assertEquals(Configuration.ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY, cfg.getAutoEscapingPolicy());
cfg.setSetting(Configuration.AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE, "enableIfDefault");
assertEquals(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY, cfg.getAutoEscapingPolicy());
cfg.setSetting(Configuration.AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE, "enable_if_default");
assertEquals(Configuration.ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY, cfg.getAutoEscapingPolicy());
cfg.setSetting(Configuration.AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE, "disable");
assertEquals(Configuration.DISABLE_AUTO_ESCAPING_POLICY, cfg.getAutoEscapingPolicy());
try {
cfg.setAutoEscapingPolicy(Configuration.CAMEL_CASE_NAMING_CONVENTION);
fail();
} catch (IllegalArgumentException e) {
// Expected
}
}
public void testSetOutputFormat() throws Exception {
Configuration cfg = new Configuration();
assertEquals(UndefinedOutputFormat.INSTANCE, cfg.getOutputFormat());
assertFalse(cfg.isOutputFormatExplicitlySet());
try {
cfg.setOutputFormat(null);
fail();
} catch (NullArgumentException e) {
// Expected
}
assertFalse(cfg.isOutputFormatExplicitlySet());
cfg.setSetting(Configuration.OUTPUT_FORMAT_KEY_CAMEL_CASE, XMLOutputFormat.class.getSimpleName());
assertEquals(XMLOutputFormat.INSTANCE, cfg.getOutputFormat());
cfg.setSetting(Configuration.OUTPUT_FORMAT_KEY_SNAKE_CASE, HTMLOutputFormat.class.getSimpleName());
assertEquals(HTMLOutputFormat.INSTANCE, cfg.getOutputFormat());
// Set standard format by name instead of class name:
cfg.setSetting(Configuration.OUTPUT_FORMAT_KEY_CAMEL_CASE, XHTMLOutputFormat.INSTANCE.getName());
assertEquals(XHTMLOutputFormat.INSTANCE, cfg.getOutputFormat());
cfg.unsetOutputFormat();
assertEquals(UndefinedOutputFormat.INSTANCE, cfg.getOutputFormat());
assertFalse(cfg.isOutputFormatExplicitlySet());
cfg.setOutputFormat(UndefinedOutputFormat.INSTANCE);
assertTrue(cfg.isOutputFormatExplicitlySet());
cfg.setSetting(Configuration.OUTPUT_FORMAT_KEY_CAMEL_CASE, "default");
assertFalse(cfg.isOutputFormatExplicitlySet());
try {
cfg.setSetting(Configuration.OUTPUT_FORMAT_KEY, "null");
} catch (SettingValueAssignmentException e) {
assertThat(e.getCause().getMessage(), containsString(UndefinedOutputFormat.class.getSimpleName()));
}
}
@Test
public void testGetOutputFormatByName() throws Exception {
Configuration cfg = new Configuration();
assertSame(HTMLOutputFormat.INSTANCE, cfg.getOutputFormat(HTMLOutputFormat.INSTANCE.getName()));
try {
cfg.getOutputFormat("noSuchFormat");
fail();
} catch (UnregisteredOutputFormatException e) {
assertThat(e.getMessage(), containsString("noSuchFormat"));
}
try {
cfg.getOutputFormat("HTML}");
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("'{'"));
}
{
OutputFormat of = cfg.getOutputFormat("HTML{RTF}");
assertThat(of, instanceOf(CombinedMarkupOutputFormat.class));
CombinedMarkupOutputFormat combinedOF = (CombinedMarkupOutputFormat) of;
assertSame(HTMLOutputFormat.INSTANCE, combinedOF.getOuterOutputFormat());
assertSame(RTFOutputFormat.INSTANCE, combinedOF.getInnerOutputFormat());
}
{
OutputFormat of = cfg.getOutputFormat("XML{HTML{RTF}}");
assertThat(of, instanceOf(CombinedMarkupOutputFormat.class));
CombinedMarkupOutputFormat combinedOF = (CombinedMarkupOutputFormat) of;
assertSame(XMLOutputFormat.INSTANCE, combinedOF.getOuterOutputFormat());
MarkupOutputFormat innerOF = combinedOF.getInnerOutputFormat();
assertThat(innerOF, instanceOf(CombinedMarkupOutputFormat.class));
CombinedMarkupOutputFormat innerCombinedOF = (CombinedMarkupOutputFormat) innerOF;
assertSame(HTMLOutputFormat.INSTANCE, innerCombinedOF.getOuterOutputFormat());
assertSame(RTFOutputFormat.INSTANCE, innerCombinedOF.getInnerOutputFormat());
}
try {
cfg.getOutputFormat("plainText{HTML}");
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), allOf(containsString("plainText"), containsString("markup")));
}
try {
cfg.getOutputFormat("HTML{plainText}");
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), allOf(containsString("plainText"), containsString("markup")));
}
}
public void testSetRegisteredCustomOutputFormats() throws Exception {
Configuration cfg = new Configuration();
assertTrue(cfg.getRegisteredCustomOutputFormats().isEmpty());
cfg.setSetting(Configuration.REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_CAMEL_CASE,
"[freemarker.core.CustomHTMLOutputFormat(), freemarker.core.DummyOutputFormat()]");
assertEquals(
ImmutableList.of(CustomHTMLOutputFormat.INSTANCE, DummyOutputFormat.INSTANCE),
new ArrayList(cfg.getRegisteredCustomOutputFormats()));
try {
cfg.setSetting(Configuration.REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE, "[TemplateConfiguration()]");
fail();
} catch (Exception e) {
assertThat(e.getCause().getMessage(), containsString(OutputFormat.class.getSimpleName()));
}
}
public void testSetRecognizeStandardFileExtensions() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
assertFalse(cfg.getRecognizeStandardFileExtensions());
assertFalse(cfg.isRecognizeStandardFileExtensionsExplicitlySet());
cfg.setRecognizeStandardFileExtensions(true);
assertTrue(cfg.getRecognizeStandardFileExtensions());
assertTrue(cfg.isRecognizeStandardFileExtensionsExplicitlySet());
cfg.unsetRecognizeStandardFileExtensions();
assertFalse(cfg.getRecognizeStandardFileExtensions());
assertFalse(cfg.isRecognizeStandardFileExtensionsExplicitlySet());
cfg.setSetting(Configuration.RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE, "false");
assertFalse(cfg.getRecognizeStandardFileExtensions());
assertTrue(cfg.isRecognizeStandardFileExtensionsExplicitlySet());
cfg.setSetting(Configuration.RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_SNAKE_CASE, "default");
assertFalse(cfg.getRecognizeStandardFileExtensions());
assertFalse(cfg.isRecognizeStandardFileExtensionsExplicitlySet());
cfg.unsetRecognizeStandardFileExtensions();
assertFalse(cfg.getRecognizeStandardFileExtensions());
cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_24);
assertTrue(cfg.getRecognizeStandardFileExtensions());
assertFalse(cfg.isRecognizeStandardFileExtensionsExplicitlySet());
cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_23);
assertFalse(cfg.getRecognizeStandardFileExtensions());
cfg.setRecognizeStandardFileExtensions(false);
cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_24);
assertFalse(cfg.getRecognizeStandardFileExtensions());
assertTrue(cfg.isRecognizeStandardFileExtensionsExplicitlySet());
}
public void testSetTimeZone() throws TemplateException {
TimeZone origSysDefTZ = TimeZone.getDefault();
try {
TimeZone sysDefTZ = TimeZone.getTimeZone("GMT-01");
TimeZone.setDefault(sysDefTZ);
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertEquals(sysDefTZ, cfg.getTimeZone());
assertEquals(sysDefTZ.getID(), cfg.getSetting(Configurable.TIME_ZONE_KEY));
cfg.setSetting(Configurable.TIME_ZONE_KEY, "JVM default");
assertEquals(sysDefTZ, cfg.getTimeZone());
assertEquals(sysDefTZ.getID(), cfg.getSetting(Configurable.TIME_ZONE_KEY));
TimeZone newSysDefTZ = TimeZone.getTimeZone("GMT+09");
TimeZone.setDefault(newSysDefTZ);
assertEquals(sysDefTZ, cfg.getTimeZone());
assertEquals(sysDefTZ.getID(), cfg.getSetting(Configurable.TIME_ZONE_KEY));
cfg.setSetting(Configurable.TIME_ZONE_KEY, "JVM default");
assertEquals(newSysDefTZ, cfg.getTimeZone());
assertEquals(newSysDefTZ.getID(), cfg.getSetting(Configurable.TIME_ZONE_KEY));
} finally {
TimeZone.setDefault(origSysDefTZ);
}
}
public void testSetSQLDateAndTimeTimeZone() throws TemplateException {
TimeZone origSysDefTZ = TimeZone.getDefault();
try {
TimeZone sysDefTZ = TimeZone.getTimeZone("GMT-01");
TimeZone.setDefault(sysDefTZ);
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertNull(cfg.getSQLDateAndTimeTimeZone());
assertEquals("null", cfg.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
cfg.setSQLDateAndTimeTimeZone(null);
assertNull(cfg.getSQLDateAndTimeTimeZone());
assertEquals("null", cfg.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
cfg.setSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY, "JVM default");
assertEquals(sysDefTZ, cfg.getSQLDateAndTimeTimeZone());
assertEquals(sysDefTZ.getID(), cfg.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
cfg.setSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY, "null");
assertNull(cfg.getSQLDateAndTimeTimeZone());
assertEquals("null", cfg.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
} finally {
TimeZone.setDefault(origSysDefTZ);
}
}
public void testTimeZoneLayers() throws TemplateException, IOException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
Template t = new Template(null, "", cfg);
Environment env1 = t.createProcessingEnvironment(null, new StringWriter());
Environment env2 = t.createProcessingEnvironment(null, new StringWriter());
// cfg:
assertEquals(TimeZone.getDefault(), cfg.getTimeZone());
assertNull(cfg.getSQLDateAndTimeTimeZone());
assertEquals("null", cfg.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
// env:
assertEquals(TimeZone.getDefault(), env1.getTimeZone());
assertNull(env1.getSQLDateAndTimeTimeZone());
assertEquals("null", env1.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
// env 2:
assertEquals(TimeZone.getDefault(), env2.getTimeZone());
assertNull(env2.getSQLDateAndTimeTimeZone());
assertEquals("null", env2.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
env1.setSQLDateAndTimeTimeZone(DateUtil.UTC);
// cfg:
assertEquals(TimeZone.getDefault(), cfg.getTimeZone());
assertNull(cfg.getSQLDateAndTimeTimeZone());
assertEquals("null", cfg.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
// env:
assertEquals(TimeZone.getDefault(), env1.getTimeZone());
assertEquals(DateUtil.UTC, env1.getSQLDateAndTimeTimeZone());
assertEquals(DateUtil.UTC.getID(), env1.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
TimeZone localTZ = TimeZone.getTimeZone("Europe/Brussels");
env1.setTimeZone(localTZ);
// cfg:
assertEquals(TimeZone.getDefault(), cfg.getTimeZone());
assertNull(cfg.getSQLDateAndTimeTimeZone());
assertEquals("null", cfg.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
// env:
assertEquals(localTZ, env1.getTimeZone());
assertEquals(DateUtil.UTC, env1.getSQLDateAndTimeTimeZone());
assertEquals(DateUtil.UTC.getID(), env1.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
// env 2:
assertEquals(TimeZone.getDefault(), env2.getTimeZone());
assertNull(env2.getSQLDateAndTimeTimeZone());
assertEquals("null", env2.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
TimeZone otherTZ1 = TimeZone.getTimeZone("GMT+05");
TimeZone otherTZ2 = TimeZone.getTimeZone("GMT+06");
cfg.setTimeZone(otherTZ1);
cfg.setSQLDateAndTimeTimeZone(otherTZ2);
// cfg:
assertEquals(otherTZ1, cfg.getTimeZone());
assertEquals(otherTZ2, cfg.getSQLDateAndTimeTimeZone());
assertEquals(otherTZ1.getID(), cfg.getSetting(Configurable.TIME_ZONE_KEY));
assertEquals(otherTZ2.getID(), cfg.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
// env:
assertEquals(localTZ, env1.getTimeZone());
assertEquals(DateUtil.UTC, env1.getSQLDateAndTimeTimeZone());
assertEquals(DateUtil.UTC.getID(), env1.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
// env 2:
assertEquals(otherTZ1, env2.getTimeZone());
assertEquals(otherTZ2, env2.getSQLDateAndTimeTimeZone());
assertEquals(otherTZ1.getID(), env2.getSetting(Configurable.TIME_ZONE_KEY));
assertEquals(otherTZ2.getID(), env2.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
try {
setTimeZoneToNull(env2);
fail();
} catch (IllegalArgumentException e) {
// expected
}
env2.setSQLDateAndTimeTimeZone(null);
assertEquals(otherTZ1, env2.getTimeZone());
assertNull(env2.getSQLDateAndTimeTimeZone());
assertEquals(otherTZ1.getID(), env2.getSetting(Configurable.TIME_ZONE_KEY));
assertEquals("null", env2.getSetting(Configurable.SQL_DATE_AND_TIME_TIME_ZONE_KEY));
}
@SuppressFBWarnings(value="NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS", justification="Expected to fail")
private void setTimeZoneToNull(Environment env2) {
env2.setTimeZone(null);
}
public void testSetICIViaSetSettingAPI() throws TemplateException {
Configuration cfg = new Configuration();
assertEquals(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS, cfg.getIncompatibleImprovements());
cfg.setSetting(Configuration.INCOMPATIBLE_IMPROVEMENTS, "2.3.21");
assertEquals(Configuration.VERSION_2_3_21, cfg.getIncompatibleImprovements());
}
public void testSetLogTemplateExceptionsViaSetSettingAPI() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertEquals(true, cfg.getLogTemplateExceptions());
cfg.setSetting(Configurable.LOG_TEMPLATE_EXCEPTIONS_KEY, "false");
assertEquals(false, cfg.getLogTemplateExceptions());
}
public void testSetWrapUncheckedExceptionsViaSetSettingAPI() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_27);
assertEquals(false, cfg.getWrapUncheckedExceptions());
cfg.setSetting(Configurable.WRAP_UNCHECKED_EXCEPTIONS_KEY_CAMEL_CASE, "true");
assertEquals(true, cfg.getWrapUncheckedExceptions());
cfg.setSetting(Configurable.WRAP_UNCHECKED_EXCEPTIONS_KEY_SNAKE_CASE, "false");
assertEquals(false, cfg.getWrapUncheckedExceptions());
}
public void testSetAttemptExceptionReporter() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertEquals(AttemptExceptionReporter.LOG_ERROR_REPORTER, cfg.getAttemptExceptionReporter());
assertFalse(cfg.isAttemptExceptionReporterExplicitlySet());
cfg.setSetting(Configurable.ATTEMPT_EXCEPTION_REPORTER_KEY, "log_warn");
assertEquals(AttemptExceptionReporter.LOG_WARN_REPORTER, cfg.getAttemptExceptionReporter());
assertTrue(cfg.isAttemptExceptionReporterExplicitlySet());
cfg.setSetting(Configurable.ATTEMPT_EXCEPTION_REPORTER_KEY, "default");
assertEquals(AttemptExceptionReporter.LOG_ERROR_REPORTER, cfg.getAttemptExceptionReporter());
assertFalse(cfg.isAttemptExceptionReporterExplicitlySet());
}
public void testSharedVariables() throws TemplateModelException {
Configuration cfg = new Configuration();
cfg.setSharedVariable("erased", "");
assertNotNull(cfg.getSharedVariable("erased"));
Map<String, Object> vars = new HashMap<>();
vars.put("a", "aa");
vars.put("b", "bb");
vars.put("c", new MyScalarModel());
cfg.setSharedVaribles(vars);
assertNull(cfg.getSharedVariable("erased"));
{
TemplateScalarModel aVal = (TemplateScalarModel) cfg.getSharedVariable("a");
assertEquals("aa", aVal.getAsString());
assertEquals(SimpleScalar.class, aVal.getClass());
TemplateScalarModel bVal = (TemplateScalarModel) cfg.getSharedVariable("b");
assertEquals("bb", bVal.getAsString());
assertEquals(SimpleScalar.class, bVal.getClass());
TemplateScalarModel cVal = (TemplateScalarModel) cfg.getSharedVariable("c");
assertEquals("my", cVal.getAsString());
assertEquals(MyScalarModel.class, cfg.getSharedVariable("c").getClass());
}
// Legacy method: Keeps TemplateModel created on the time it was called.
cfg.setSharedVariable("b", "bbLegacy");
// Cause re-wrapping of variables added via setSharedVaribles:
cfg.setObjectWrapper(new BeansWrapperBuilder(Configuration.VERSION_2_3_0).build());
{
TemplateScalarModel aVal = (TemplateScalarModel) cfg.getSharedVariable("a");
assertEquals("aa", aVal.getAsString());
assertEquals(StringModel.class, aVal.getClass());
TemplateScalarModel bVal = (TemplateScalarModel) cfg.getSharedVariable("b");
assertEquals("bbLegacy", bVal.getAsString());
assertEquals(SimpleScalar.class, bVal.getClass());
TemplateScalarModel cVal = (TemplateScalarModel) cfg.getSharedVariable("c");
assertEquals("my", cVal.getAsString());
assertEquals(MyScalarModel.class, cVal.getClass());
}
}
@Test
public void testApiBuiltinEnabled() throws IOException, TemplateException {
for (Version v : new Version[] { Configuration.VERSION_2_3_0, Configuration.VERSION_2_3_22 }) {
Configuration cfg = new Configuration(v);
try {
new Template(null, "${1?api}", cfg).process(null, NullWriter.INSTANCE);
fail();
} catch (TemplateException e) {
assertThat(e.getMessage(), containsString(Configurable.API_BUILTIN_ENABLED_KEY));
}
}
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
cfg.setAPIBuiltinEnabled(true);
new Template(null, "${m?api.hashCode()}", cfg)
.process(Collections.singletonMap("m", new HashMap()), NullWriter.INSTANCE);
}
@Test
public void testTemplateUpdateDelay() throws IOException, TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertEquals(TemplateCache.DEFAULT_TEMPLATE_UPDATE_DELAY_MILLIS, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setTemplateUpdateDelay(4);
assertEquals(4000L, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setTemplateUpdateDelayMilliseconds(100);
assertEquals(100L, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setSetting(Configuration.TEMPLATE_UPDATE_DELAY_KEY, "5");
assertEquals(5000L, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setSetting(Configuration.TEMPLATE_UPDATE_DELAY_KEY, "3 ms");
assertEquals(3L, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setSetting(Configuration.TEMPLATE_UPDATE_DELAY_KEY, "4ms");
assertEquals(4L, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setSetting(Configuration.TEMPLATE_UPDATE_DELAY_KEY, "3 s");
assertEquals(3000L, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setSetting(Configuration.TEMPLATE_UPDATE_DELAY_KEY, "4s");
assertEquals(4000L, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setSetting(Configuration.TEMPLATE_UPDATE_DELAY_KEY, "3 m");
assertEquals(1000L * 60 * 3, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setSetting(Configuration.TEMPLATE_UPDATE_DELAY_KEY, "4m");
assertEquals(1000L * 60 * 4, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setSetting(Configuration.TEMPLATE_UPDATE_DELAY_KEY, "1 h");
assertEquals(1000L * 60 * 60, cfg.getTemplateUpdateDelayMilliseconds());
cfg.setSetting(Configuration.TEMPLATE_UPDATE_DELAY_KEY, "2h");
assertEquals(1000L * 60 * 60 * 2, cfg.getTemplateUpdateDelayMilliseconds());
}
@Test
@SuppressFBWarnings(value = "NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS ", justification = "Testing wrong args")
public void testSetCustomNumberFormat() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
try {
cfg.setCustomNumberFormats(null);
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("null"));
}
try {
cfg.setCustomNumberFormats(Collections.singletonMap("", HexTemplateNumberFormatFactory.INSTANCE));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("0 length"));
}
try {
cfg.setCustomNumberFormats(Collections.singletonMap("a_b", HexTemplateNumberFormatFactory.INSTANCE));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("a_b"));
}
try {
cfg.setCustomNumberFormats(Collections.singletonMap("a b", HexTemplateNumberFormatFactory.INSTANCE));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("a b"));
}
try {
cfg.setCustomNumberFormats(ImmutableMap.of(
"a", HexTemplateNumberFormatFactory.INSTANCE,
"@wrong", HexTemplateNumberFormatFactory.INSTANCE));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("@wrong"));
}
cfg.setSetting(Configurable.CUSTOM_NUMBER_FORMATS_KEY_CAMEL_CASE,
"{ 'base': " + BaseNTemplateNumberFormatFactory.class.getName() + "() }");
assertEquals(
Collections.singletonMap("base", BaseNTemplateNumberFormatFactory.INSTANCE),
cfg.getCustomNumberFormats());
cfg.setSetting(Configurable.CUSTOM_NUMBER_FORMATS_KEY_SNAKE_CASE,
"{ "
+ "'base': " + BaseNTemplateNumberFormatFactory.class.getName() + "(), "
+ "'hex': " + HexTemplateNumberFormatFactory.class.getName() + "()"
+ " }");
assertEquals(
ImmutableMap.of(
"base", BaseNTemplateNumberFormatFactory.INSTANCE,
"hex", HexTemplateNumberFormatFactory.INSTANCE),
cfg.getCustomNumberFormats());
cfg.setSetting(Configurable.CUSTOM_NUMBER_FORMATS_KEY, "{}");
assertEquals(Collections.emptyMap(), cfg.getCustomNumberFormats());
try {
cfg.setSetting(Configurable.CUSTOM_NUMBER_FORMATS_KEY_CAMEL_CASE,
"{ 'x': " + EpochMillisTemplateDateFormatFactory.class.getName() + "() }");
fail();
} catch (TemplateException e) {
assertThat(e.getCause().getMessage(), allOf(
containsString(EpochMillisTemplateDateFormatFactory.class.getName()),
containsString(TemplateNumberFormatFactory.class.getName())));
}
}
@Test
public void testSetBooleanFormat() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
cfg.setBooleanFormat("yes,no");
assertOutputEquals("yes no", new Template(null, "${true} ${false}", cfg));
cfg.setBooleanFormat("c");
assertOutputEquals("true false", new Template(null, "${true} ${false}", cfg));
try {
cfg.setBooleanFormat("yes no");
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("comma"));
}
}
@Test
public void testSetTabSize() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
String ftl = "${\t}";
try {
new Template(null, ftl, cfg);
fail();
} catch (ParseException e) {
assertEquals(9, e.getColumnNumber());
}
cfg.setTabSize(1);
try {
new Template(null, ftl, cfg);
fail();
} catch (ParseException e) {
assertEquals(4, e.getColumnNumber());
}
try {
cfg.setTabSize(0);
fail();
} catch (IllegalArgumentException e) {
// Expected
}
try {
cfg.setTabSize(257);
fail();
} catch (IllegalArgumentException e) {
// Expected
}
}
@Test
public void testTabSizeSetting() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertEquals(8, cfg.getTabSize());
cfg.setSetting(Configuration.TAB_SIZE_KEY_CAMEL_CASE, "4");
assertEquals(4, cfg.getTabSize());
cfg.setSetting(Configuration.TAB_SIZE_KEY_SNAKE_CASE, "1");
assertEquals(1, cfg.getTabSize());
try {
cfg.setSetting(Configuration.TAB_SIZE_KEY_SNAKE_CASE, "x");
fail();
} catch (TemplateException e) {
assertThat(e.getCause(), instanceOf(NumberFormatException.class));
}
}
@SuppressFBWarnings(value="NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS", justification="We test failures")
@Test
public void testSetCustomDateFormat() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
try {
cfg.setCustomDateFormats(null);
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("null"));
}
try {
cfg.setCustomDateFormats(Collections.singletonMap("", EpochMillisTemplateDateFormatFactory.INSTANCE));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("0 length"));
}
try {
cfg.setCustomDateFormats(Collections.singletonMap("a_b", EpochMillisTemplateDateFormatFactory.INSTANCE));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("a_b"));
}
try {
cfg.setCustomDateFormats(Collections.singletonMap("a b", EpochMillisTemplateDateFormatFactory.INSTANCE));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("a b"));
}
try {
cfg.setCustomDateFormats(ImmutableMap.of(
"a", EpochMillisTemplateDateFormatFactory.INSTANCE,
"@wrong", EpochMillisTemplateDateFormatFactory.INSTANCE));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("@wrong"));
}
cfg.setSetting(Configurable.CUSTOM_DATE_FORMATS_KEY_CAMEL_CASE,
"{ 'epoch': " + EpochMillisTemplateDateFormatFactory.class.getName() + "() }");
assertEquals(
Collections.singletonMap("epoch", EpochMillisTemplateDateFormatFactory.INSTANCE),
cfg.getCustomDateFormats());
cfg.setSetting(Configurable.CUSTOM_DATE_FORMATS_KEY_SNAKE_CASE,
"{ "
+ "'epoch': " + EpochMillisTemplateDateFormatFactory.class.getName() + "(), "
+ "'epochDiv': " + EpochMillisDivTemplateDateFormatFactory.class.getName() + "()"
+ " }");
assertEquals(
ImmutableMap.of(
"epoch", EpochMillisTemplateDateFormatFactory.INSTANCE,
"epochDiv", EpochMillisDivTemplateDateFormatFactory.INSTANCE),
cfg.getCustomDateFormats());
cfg.setSetting(Configurable.CUSTOM_DATE_FORMATS_KEY, "{}");
assertEquals(Collections.emptyMap(), cfg.getCustomDateFormats());
try {
cfg.setSetting(Configurable.CUSTOM_DATE_FORMATS_KEY_CAMEL_CASE,
"{ 'x': " + HexTemplateNumberFormatFactory.class.getName() + "() }");
fail();
} catch (TemplateException e) {
assertThat(e.getCause().getMessage(), allOf(
containsString(HexTemplateNumberFormatFactory.class.getName()),
containsString(TemplateDateFormatFactory.class.getName())));
}
}
@Test
public void testHasCustomFormats() throws IOException, TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
Template t = new Template(null, "", cfg);
Environment env = t.createProcessingEnvironment(null, null);
assertFalse(cfg.hasCustomFormats());
assertFalse(t.hasCustomFormats());
assertFalse(env.hasCustomFormats());
env.setCustomDateFormats(Collections.singletonMap("f", EpochMillisTemplateDateFormatFactory.INSTANCE));
assertFalse(t.hasCustomFormats());
assertTrue(env.hasCustomFormats());
t.setCustomDateFormats(Collections.singletonMap("f", EpochMillisTemplateDateFormatFactory.INSTANCE));
assertFalse(cfg.hasCustomFormats());
assertTrue(t.hasCustomFormats());
cfg.setCustomDateFormats(Collections.singletonMap("f", EpochMillisTemplateDateFormatFactory.INSTANCE));
assertTrue(cfg.hasCustomFormats());
assertTrue(t.hasCustomFormats());
assertTrue(env.hasCustomFormats());
cfg.setCustomDateFormats(Collections.<String, TemplateDateFormatFactory>emptyMap());
t.setCustomDateFormats(Collections.<String, TemplateDateFormatFactory>emptyMap());
env.setCustomDateFormats(Collections.<String, TemplateDateFormatFactory>emptyMap());
assertFalse(cfg.hasCustomFormats());
assertFalse(t.hasCustomFormats());
assertFalse(env.hasCustomFormats());
cfg.setCustomDateFormats(Collections.singletonMap("f", EpochMillisTemplateDateFormatFactory.INSTANCE));
assertTrue(cfg.hasCustomFormats());
assertTrue(t.hasCustomFormats());
assertTrue(env.hasCustomFormats());
cfg.setCustomDateFormats(Collections.<String, TemplateDateFormatFactory>emptyMap());
t.setCustomDateFormats(Collections.<String, TemplateDateFormatFactory>emptyMap());
env.setCustomDateFormats(Collections.<String, TemplateDateFormatFactory>emptyMap());
assertFalse(cfg.hasCustomFormats());
assertFalse(t.hasCustomFormats());
assertFalse(env.hasCustomFormats());
// Same with number formats:
env.setCustomNumberFormats(Collections.singletonMap("f", HexTemplateNumberFormatFactory.INSTANCE));
assertFalse(t.hasCustomFormats());
assertTrue(env.hasCustomFormats());
t.setCustomNumberFormats(Collections.singletonMap("f", HexTemplateNumberFormatFactory.INSTANCE));
assertFalse(cfg.hasCustomFormats());
assertTrue(t.hasCustomFormats());
cfg.setCustomNumberFormats(Collections.singletonMap("f", HexTemplateNumberFormatFactory.INSTANCE));
assertTrue(cfg.hasCustomFormats());
assertTrue(t.hasCustomFormats());
assertTrue(env.hasCustomFormats());
cfg.setCustomNumberFormats(Collections.<String, TemplateNumberFormatFactory>emptyMap());
t.setCustomNumberFormats(Collections.<String, TemplateNumberFormatFactory>emptyMap());
env.setCustomNumberFormats(Collections.<String, TemplateNumberFormatFactory>emptyMap());
assertFalse(cfg.hasCustomFormats());
assertFalse(t.hasCustomFormats());
assertFalse(env.hasCustomFormats());
cfg.setCustomNumberFormats(Collections.singletonMap("f", HexTemplateNumberFormatFactory.INSTANCE));
assertTrue(cfg.hasCustomFormats());
assertTrue(t.hasCustomFormats());
assertTrue(env.hasCustomFormats());
}
public void testNamingConventionSetSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertEquals(Configuration.AUTO_DETECT_NAMING_CONVENTION, cfg.getNamingConvention());
cfg.setSetting("naming_convention", "legacy");
assertEquals(Configuration.LEGACY_NAMING_CONVENTION, cfg.getNamingConvention());
cfg.setSetting("naming_convention", "camel_case");
assertEquals(Configuration.CAMEL_CASE_NAMING_CONVENTION, cfg.getNamingConvention());
cfg.setSetting("naming_convention", "auto_detect");
assertEquals(Configuration.AUTO_DETECT_NAMING_CONVENTION, cfg.getNamingConvention());
}
public void testTagSyntaxSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
// Default is "angle brackets":
assertEquals(Configuration.ANGLE_BRACKET_TAG_SYNTAX, cfg.getTagSyntax());
cfg.setSetting("tag_syntax", "angle_bracket");
assertEquals(Configuration.ANGLE_BRACKET_TAG_SYNTAX, cfg.getTagSyntax());
cfg.setSetting("tag_syntax", "square_bracket");
assertEquals(Configuration.SQUARE_BRACKET_TAG_SYNTAX, cfg.getTagSyntax());
cfg.setSetting("tag_syntax", "auto_detect");
assertEquals(Configuration.AUTO_DETECT_TAG_SYNTAX, cfg.getTagSyntax());
// Camel case:
cfg.setSetting("tagSyntax", "squareBracket");
assertEquals(Configuration.SQUARE_BRACKET_TAG_SYNTAX, cfg.getTagSyntax());
try {
cfg.setSetting("tag_syntax", "no_such_syntax");
fail();
} catch (TemplateException e) {
assertThat(e.getMessage(), containsString("no_such_syntax"));
}
// Catch an oversight that's easy to do:
try {
cfg.setTagSyntax(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX);
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("SQUARE_BRACKET_TAG_SYNTAX"));
}
}
public void testInterpolationSyntaxSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
// Default is "legacy":
assertEquals(Configuration.LEGACY_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
cfg.setSetting("interpolation_syntax", "dollar");
assertEquals(Configuration.DOLLAR_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
cfg.setSetting("interpolation_syntax", "square_bracket");
assertEquals(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
cfg.setSetting("interpolation_syntax", "legacy");
assertEquals(Configuration.LEGACY_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
// Camel case:
cfg.setSetting("interpolationSyntax", "squareBracket");
assertEquals(Configuration.SQUARE_BRACKET_INTERPOLATION_SYNTAX, cfg.getInterpolationSyntax());
try {
cfg.setSetting("interpolation_syntax", "no_such_syntax");
fail();
} catch (TemplateException e) {
assertThat(e.getMessage(), containsString("no_such_syntax"));
}
// Catch an oversight that's easy to do:
try {
cfg.setInterpolationSyntax(Configuration.SQUARE_BRACKET_TAG_SYNTAX);
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("SQUARE_BRACKET_INTERPOLATION_SYNTAX"));
}
}
public void testLazyImportsSetSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertFalse(cfg.getLazyImports());
assertTrue(cfg.isLazyImportsSet());
cfg.setSetting("lazy_imports", "true");
assertTrue(cfg.getLazyImports());
cfg.setSetting("lazyImports", "false");
assertFalse(cfg.getLazyImports());
}
public void testLazyAutoImportsSetSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertNull(cfg.getLazyAutoImports());
assertTrue(cfg.isLazyAutoImportsSet());
cfg.setSetting("lazy_auto_imports", "true");
assertEquals(Boolean.TRUE, cfg.getLazyAutoImports());
assertTrue(cfg.isLazyAutoImportsSet());
cfg.setSetting("lazyAutoImports", "false");
assertEquals(Boolean.FALSE, cfg.getLazyAutoImports());
cfg.setSetting("lazyAutoImports", "null");
assertNull(cfg.getLazyAutoImports());
assertTrue(cfg.isLazyAutoImportsSet());
}
public void testLocaleSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertEquals(Locale.getDefault(), cfg.getLocale());
assertFalse(cfg.isLocaleExplicitlySet());
Locale nonDefault = Locale.getDefault().equals(Locale.GERMANY) ? Locale.FRANCE : Locale.GERMANY;
cfg.setLocale(nonDefault);
assertTrue(cfg.isLocaleExplicitlySet());
assertEquals(nonDefault, cfg.getLocale());
cfg.unsetLocale();
assertEquals(Locale.getDefault(), cfg.getLocale());
assertFalse(cfg.isLocaleExplicitlySet());
cfg.setSetting(Configuration.LOCALE_KEY, "JVM default");
assertEquals(Locale.getDefault(), cfg.getLocale());
assertTrue(cfg.isLocaleExplicitlySet());
}
public void testDefaultEncodingSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
String defaultFileEncoding = System.getProperty("file.encoding");
assertNotNull(defaultFileEncoding);
assertEquals(defaultFileEncoding, cfg.getDefaultEncoding());
assertFalse(cfg.isDefaultEncodingExplicitlySet());
String nonDefault = defaultFileEncoding.equalsIgnoreCase("UTF-8") ? "ISO-8859-1" : "UTF-8";
cfg.setDefaultEncoding(nonDefault);
assertTrue(cfg.isDefaultEncodingExplicitlySet());
assertEquals(nonDefault, cfg.getDefaultEncoding());
cfg.unsetDefaultEncoding();
assertEquals(defaultFileEncoding, cfg.getDefaultEncoding());
assertFalse(cfg.isDefaultEncodingExplicitlySet());
cfg.setSetting(Configuration.DEFAULT_ENCODING_KEY, "JVM default");
assertEquals(defaultFileEncoding, cfg.getDefaultEncoding());
assertTrue(cfg.isDefaultEncodingExplicitlySet());
}
public void testTimeZoneSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertEquals(TimeZone.getDefault(), cfg.getTimeZone());
assertFalse(cfg.isTimeZoneExplicitlySet());
TimeZone nonDefault = TimeZone.getDefault().equals(DateUtil.UTC) ? TimeZone.getTimeZone("PST") : DateUtil.UTC;
cfg.setTimeZone(nonDefault);
assertTrue(cfg.isTimeZoneExplicitlySet());
assertEquals(nonDefault, cfg.getTimeZone());
cfg.unsetTimeZone();
assertEquals(TimeZone.getDefault(), cfg.getTimeZone());
assertFalse(cfg.isTimeZoneExplicitlySet());
cfg.setSetting(Configuration.TIME_ZONE_KEY, "JVM default");
assertEquals(TimeZone.getDefault(), cfg.getTimeZone());
assertTrue(cfg.isTimeZoneExplicitlySet());
}
public void testNewBuiltinClassResolverSetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertSame(TemplateClassResolver.UNRESTRICTED_RESOLVER, cfg.getNewBuiltinClassResolver());
cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE,
"allowed_classes: com.example.C1, com.example.C2, trusted_templates: lib/*, safe.ftl");
assertThat(cfg.getNewBuiltinClassResolver(), instanceOf(OptInTemplateClassResolver.class));
cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE, "safer");
assertSame(TemplateClassResolver.SAFER_RESOLVER, cfg.getNewBuiltinClassResolver());
cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_CAMEL_CASE,
"allowedClasses: com.example.C1, com.example.C2, trustedTemplates: lib/*, safe.ftl");
assertThat(cfg.getNewBuiltinClassResolver(), instanceOf(OptInTemplateClassResolver.class));
cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE, "allowsNothing");
assertSame(TemplateClassResolver.ALLOWS_NOTHING_RESOLVER, cfg.getNewBuiltinClassResolver());
cfg.setSetting(Configuration.NEW_BUILTIN_CLASS_RESOLVER_KEY_SNAKE_CASE, "allows_nothing");
assertSame(TemplateClassResolver.ALLOWS_NOTHING_RESOLVER, cfg.getNewBuiltinClassResolver());
}
public void testTruncateBuiltinAlgorithm() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
assertSame(DefaultTruncateBuiltinAlgorithm.ASCII_INSTANCE, cfg.getTruncateBuiltinAlgorithm());
cfg.setSetting("truncateBuiltinAlgorithm", "unicodE");
assertSame(DefaultTruncateBuiltinAlgorithm.UNICODE_INSTANCE, cfg.getTruncateBuiltinAlgorithm());
cfg.setSetting("truncate_builtin_algorithm", "ASCII");
assertSame(DefaultTruncateBuiltinAlgorithm.ASCII_INSTANCE, cfg.getTruncateBuiltinAlgorithm());
{
cfg.setSetting("truncate_builtin_algorithm",
"DefaultTruncateBuiltinAlgorithm('...', false)");
DefaultTruncateBuiltinAlgorithm alg =
(DefaultTruncateBuiltinAlgorithm) cfg.getTruncateBuiltinAlgorithm();
assertEquals("...", alg.getDefaultTerminator());
assertFalse(alg.getAddSpaceAtWordBoundary());
assertEquals(3, alg.getDefaultTerminatorLength());
assertNull(alg.getDefaultMTerminator());
assertNull(alg.getDefaultMTerminatorLength());
assertEquals(DefaultTruncateBuiltinAlgorithm.DEFAULT_WORD_BOUNDARY_MIN_LENGTH,
alg.getWordBoundaryMinLength());
}
{
cfg.setSetting("truncate_builtin_algorithm",
"DefaultTruncateBuiltinAlgorithm(" +
"'...', " +
"markup(HTMLOutputFormat(), '<span class=trunc>...</span>'), " +
"true)");
DefaultTruncateBuiltinAlgorithm alg =
(DefaultTruncateBuiltinAlgorithm) cfg.getTruncateBuiltinAlgorithm();
assertEquals("...", alg.getDefaultTerminator());
assertTrue(alg.getAddSpaceAtWordBoundary());
assertEquals(3, alg.getDefaultTerminatorLength());
assertEquals("markupOutput(format=HTML, markup=<span class=trunc>...</span>)",
alg.getDefaultMTerminator().toString());
assertEquals(Integer.valueOf(3), alg.getDefaultMTerminatorLength());
assertEquals(DefaultTruncateBuiltinAlgorithm.DEFAULT_WORD_BOUNDARY_MIN_LENGTH,
alg.getWordBoundaryMinLength());
}
{
cfg.setSetting("truncate_builtin_algorithm",
"DefaultTruncateBuiltinAlgorithm(" +
"DefaultTruncateBuiltinAlgorithm.STANDARD_ASCII_TERMINATOR, null, null, " +
"DefaultTruncateBuiltinAlgorithm.STANDARD_M_TERMINATOR, null, null, " +
"true, 0.5)");
DefaultTruncateBuiltinAlgorithm alg =
(DefaultTruncateBuiltinAlgorithm) cfg.getTruncateBuiltinAlgorithm();
assertEquals(DefaultTruncateBuiltinAlgorithm.STANDARD_ASCII_TERMINATOR, alg.getDefaultTerminator());
assertTrue(alg.getAddSpaceAtWordBoundary());
assertEquals(5, alg.getDefaultTerminatorLength());
assertEquals(DefaultTruncateBuiltinAlgorithm.STANDARD_M_TERMINATOR.toString(),
alg.getDefaultMTerminator().toString());
assertEquals(Integer.valueOf(3), alg.getDefaultMTerminatorLength());
assertEquals(0.5, alg.getWordBoundaryMinLength());
}
}
@Test
public void testFallbackOnNullLoopVariable() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_29);
assertTrue(cfg.getFallbackOnNullLoopVariable());
cfg.setSetting("fallback_on_null_loop_variable", "false");
assertFalse(cfg.getFallbackOnNullLoopVariable());
cfg.setSetting("fallback_on_null_loop_variable", "true");
assertTrue(cfg.getFallbackOnNullLoopVariable());
cfg.setSetting("fallbackOnNullLoopVariable", "NO");
assertFalse(cfg.getFallbackOnNullLoopVariable());
}
public static final MemberAccessPolicy CONFIG_TEST_MEMBER_ACCESS_POLICY;
static {
try {
CONFIG_TEST_MEMBER_ACCESS_POLICY = new WhitelistMemberAccessPolicy(MemberSelectorListMemberAccessPolicy.MemberSelector.parse(
ImmutableList.of(
File.class.getName() + ".getName()",
File.class.getName() + ".isFile()"),
false,
ConfigurationTest.class.getClassLoader()));
} catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
@Test
public void testMemberAccessPolicySetting() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
cfg.setSetting(
"objectWrapper",
"DefaultObjectWrapper(2.3.30, "
+ "memberAccessPolicy="
+ ConfigurationTest.class.getName() + ".CONFIG_TEST_MEMBER_ACCESS_POLICY"
+ ")");
TemplateHashModel m = (TemplateHashModel) cfg.getObjectWrapper().wrap(new File("x"));
assertNotNull(m.get("getName"));
assertNotNull(m.get("isFile"));
assertNull(m.get("delete"));
}
@Test
public void testMemberAccessPolicySetting2() throws TemplateException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
cfg.setSetting(
"objectWrapper",
"DefaultObjectWrapper(2.3.30, "
+ "memberAccessPolicy=" + LegacyDefaultMemberAccessPolicy.class.getName() + "())");
assertSame(((DefaultObjectWrapper) cfg.getObjectWrapper()).getMemberAccessPolicy(),
LegacyDefaultMemberAccessPolicy.INSTANCE);
}
@Test
public void testGetSettingNamesAreSorted() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
for (boolean camelCase : new boolean[] { false, true }) {
List<String> names = new ArrayList<>(cfg.getSettingNames(camelCase));
List<String> cfgableNames = new ArrayList<>(new Template(null, "", cfg).getSettingNames(camelCase));
assertStartsWith(names, cfgableNames);
String prevName = null;
for (int i = cfgableNames.size(); i < names.size(); i++) {
String name = names.get(i);
if (prevName != null) {
assertThat(name, greaterThan(prevName));
}
prevName = name;
}
}
}
@Test
public void testGetSettingNamesNameConventionsContainTheSame() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
ConfigurableTest.testGetSettingNamesNameConventionsContainTheSame(
new ArrayList<>(cfg.getSettingNames(false)),
new ArrayList<>(cfg.getSettingNames(true)));
}
@Test
public void testStaticFieldKeysCoverAllGetSettingNames() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
List<String> names = new ArrayList<>(cfg.getSettingNames(false));
List<String> cfgableNames = new ArrayList<>(cfg.getSettingNames(false));
assertStartsWith(names, cfgableNames);
for (int i = cfgableNames.size(); i < names.size(); i++) {
String name = names.get(i);
assertTrue("No field was found for " + name, keyFieldExists(name));
}
}
@Test
public void testGetSettingNamesCoversAllStaticKeyFields() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
Collection<String> names = cfg.getSettingNames(false);
for (Class<? extends Configurable> cfgableClass : new Class[] { Configuration.class, Configurable.class }) {
for (Field f : cfgableClass.getFields()) {
if (f.getName().endsWith("_KEY")) {
final Object name = f.get(null);
assertTrue("Missing setting name: " + name, names.contains(name));
}
}
}
}
@Test
public void testKeyStaticFieldsHasAllVariationsAndCorrectFormat() throws IllegalArgumentException, IllegalAccessException {
ConfigurableTest.testKeyStaticFieldsHasAllVariationsAndCorrectFormat(Configuration.class);
}
@Test
public void testGetSettingNamesCoversAllSettingNames() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
Collection<String> names = cfg.getSettingNames(false);
for (Field f : Configurable.class.getFields()) {
if (f.getName().endsWith("_KEY")) {
final Object name = f.get(null);
assertTrue("Missing setting name: " + name, names.contains(name));
}
}
}
@Test
public void testSetSettingSupportsBothNamingConventions() throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
cfg.setSetting(Configuration.DEFAULT_ENCODING_KEY_CAMEL_CASE, "UTF-16LE");
assertEquals("UTF-16LE", cfg.getDefaultEncoding());
cfg.setSetting(Configuration.DEFAULT_ENCODING_KEY_SNAKE_CASE, "UTF-8");
assertEquals("UTF-8", cfg.getDefaultEncoding());
for (String nameCC : cfg.getSettingNames(true)) {
for (String value : new String[] { "1", "default", "true" }) {
Exception resultCC = null;
try {
cfg.setSetting(nameCC, value);
} catch (Exception e) {
assertThat(e, not(instanceOf(UnknownSettingException.class)));
resultCC = e;
}
String nameSC = _CoreStringUtils.camelCaseToUnderscored(nameCC);
Exception resultSC = null;
try {
cfg.setSetting(nameSC, value);
} catch (Exception e) {
assertThat(e, not(instanceOf(UnknownSettingException.class)));
resultSC = e;
}
if (resultCC == null) {
assertNull(resultSC);
} else {
assertNotNull(resultSC);
assertEquals(resultCC.getClass(), resultSC.getClass());
}
}
}
}
@Test
public void testGetSupportedBuiltInDirectiveNames() {
Configuration cfg = new Configuration();
Set<String> allNames = cfg.getSupportedBuiltInDirectiveNames(Configuration.AUTO_DETECT_NAMING_CONVENTION);
Set<String> lNames = cfg.getSupportedBuiltInDirectiveNames(Configuration.LEGACY_NAMING_CONVENTION);
Set<String> cNames = cfg.getSupportedBuiltInDirectiveNames(Configuration.CAMEL_CASE_NAMING_CONVENTION);
checkNamingConventionNameSets(allNames, lNames, cNames);
for (String name : cNames) {
assertThat(name.toLowerCase(), isIn(lNames));
}
}
@Test
public void testGetSupportedBuiltInNames() {
Configuration cfg = new Configuration();
Set<String> allNames = cfg.getSupportedBuiltInNames(Configuration.AUTO_DETECT_NAMING_CONVENTION);
Set<String> lNames = cfg.getSupportedBuiltInNames(Configuration.LEGACY_NAMING_CONVENTION);
Set<String> cNames = cfg.getSupportedBuiltInNames(Configuration.CAMEL_CASE_NAMING_CONVENTION);
checkNamingConventionNameSets(allNames, lNames, cNames);
}
private void checkNamingConventionNameSets(Set<String> allNames, Set<String> lNames, Set<String> cNames) {
for (String name : lNames) {
assertThat(allNames, hasItem(name));
assertTrue("Should be all-lowercase: " + name, name.equals(name.toLowerCase()));
}
for (String name : cNames) {
assertThat(allNames, hasItem(name));
}
for (String name : allNames) {
assertThat(name, anyOf(isIn(lNames), isIn(cNames)));
}
assertEquals(lNames.size(), cNames.size());
}
@SuppressWarnings("boxing")
private void assertStartsWith(List<String> list, List<String> headList) {
int index = 0;
for (String name : headList) {
assertThat(index, lessThan(list.size()));
assertEquals(name, list.get(index));
index++;
}
}
private boolean keyFieldExists(String name) throws Exception {
Field field;
try {
field = Configuration.class.getField(name.toUpperCase() + "_KEY");
} catch (NoSuchFieldException e) {
return false;
}
assertEquals(name, field.get(null));
return true;
}
private static class MyScalarModel implements TemplateScalarModel {
public String getAsString() throws TemplateModelException {
return "my";
}
}
}