http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/TemplateConfigurationWithTemplateResolverTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateConfigurationWithTemplateResolverTest.java b/src/test/java/org/apache/freemarker/core/TemplateConfigurationWithTemplateResolverTest.java new file mode 100644 index 0000000..20ee4f3 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/TemplateConfigurationWithTemplateResolverTest.java @@ -0,0 +1,327 @@ +/* + * 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 org.apache.freemarker.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.util.Locale; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.CustomAttribute; +import org.apache.freemarker.core.Template; +import org.apache.freemarker.core.TemplateConfiguration; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.templateresolver.ConditionalTemplateConfigurationFactory; +import org.apache.freemarker.core.templateresolver.FileNameGlobMatcher; +import org.apache.freemarker.core.templateresolver.FirstMatchTemplateConfigurationFactory; +import org.apache.freemarker.core.templateresolver.MergingTemplateConfigurationFactory; +import org.apache.freemarker.core.templateresolver.impl.ByteArrayTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; +import org.junit.Test; + +public class TemplateConfigurationWithTemplateResolverTest { + + private static final String TEXT_WITH_ACCENTS = "pr\u00F3ba"; + + private static final CustomAttribute CUST_ATT_1 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); + private static final CustomAttribute CUST_ATT_2 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); + + @Test + public void testEncoding() throws Exception { + Configuration cfg = createCommonEncodingTesterConfig(); + + { + Template t = cfg.getTemplate("utf8.ftl"); + assertEquals("utf-8", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("utf8.ftl", "iso-8859-1"); + assertEquals("utf-8", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("utf16.ftl"); + assertEquals("utf-16", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("default.ftl"); + assertEquals("iso-8859-1", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("default.ftl", "iso-8859-5"); + assertEquals("iso-8859-5", t.getEncoding()); + assertEquals(new String(TEXT_WITH_ACCENTS.getBytes("iso-8859-1"), "iso-8859-5"), + getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("utf8-latin2.ftl"); + assertEquals("iso-8859-2", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("default-latin2.ftl"); + assertEquals("iso-8859-2", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + } + } + + @Test + public void testIncludeAndEncoding() throws Exception { + Configuration cfg = createCommonEncodingTesterConfig(); + ByteArrayTemplateLoader tl = (ByteArrayTemplateLoader) cfg.getTemplateLoader(); + tl.putTemplate("main.ftl", ( + "<#include 'utf8.ftl'>" + + "<#include 'utf16.ftl'>" + + "<#include 'default.ftl'>" + + "<#include 'utf8-latin2.ftl'>" + // With mostly ignored encoding params: + + "<#include 'utf8.ftl' encoding='utf-16'>" + + "<#include 'utf16.ftl' encoding='iso-8859-5'>" + + "<#include 'default.ftl' encoding='iso-8859-5'>" + + "<#include 'utf8-latin2.ftl' encoding='iso-8859-5'>" + ).getBytes("iso-8859-1")); + assertEquals( + TEXT_WITH_ACCENTS + TEXT_WITH_ACCENTS + TEXT_WITH_ACCENTS + TEXT_WITH_ACCENTS + + TEXT_WITH_ACCENTS + TEXT_WITH_ACCENTS + + new String(TEXT_WITH_ACCENTS.getBytes("iso-8859-1"), "iso-8859-5") + + TEXT_WITH_ACCENTS, + getTemplateOutput(cfg.getTemplate("main.ftl"))); + } + + @Test + public void testLocale() throws Exception { + Configuration cfg = new Configuration(Configuration.VERSION_3_0_0); + cfg.setLocale(Locale.US); + + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate("(de).ftl", "${.locale}"); + tl.putTemplate("default.ftl", "${.locale}"); + tl.putTemplate("(de)-fr.ftl", + ("<#ftl locale='fr_FR'>${.locale}")); + tl.putTemplate("default-fr.ftl", + ("<#ftl locale='fr_FR'>${.locale}")); + cfg.setTemplateLoader(tl); + + TemplateConfiguration tcDe = new TemplateConfiguration(); + tcDe.setLocale(Locale.GERMANY); + cfg.setTemplateConfigurations(new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*(de)*"), tcDe)); + + { + Template t = cfg.getTemplate("(de).ftl"); + assertEquals(Locale.GERMANY, t.getLocale()); + assertEquals("de_DE", getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("(de).ftl", Locale.ITALY); + assertEquals(Locale.GERMANY, t.getLocale()); + assertEquals("de_DE", getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("default.ftl"); + assertEquals(Locale.US, t.getLocale()); + assertEquals("en_US", getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("default.ftl", Locale.ITALY); + assertEquals(Locale.ITALY, t.getLocale()); + assertEquals("it_IT", getTemplateOutput(t)); + } + } + + @Test + public void testPlainText() throws Exception { + Configuration cfg = createCommonEncodingTesterConfig(); + cfg.setIncompatibleImprovements(Configuration.VERSION_3_0_0); + + TemplateConfiguration tcDE = new TemplateConfiguration(); + tcDE.setLocale(Locale.GERMANY); + TemplateConfiguration tcYN = new TemplateConfiguration(); + tcYN.setBooleanFormat("Y,N"); + cfg.setTemplateConfigurations( + new MergingTemplateConfigurationFactory( + cfg.getTemplateConfigurations(), + new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("utf16.ftl"), tcDE), + new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("utf16.ftl"), tcYN) + ) + ); + + { + Template t = cfg.getTemplate("utf8.ftl", null, null, false); + assertEquals("utf-8", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + assertEquals(Locale.US, t.getLocale()); + assertEquals("true,false", t.getBooleanFormat()); + } + { + Template t = cfg.getTemplate("utf8.ftl", null, "iso-8859-1", false); + assertEquals("utf-8", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + } + { + Template t = cfg.getTemplate("utf16.ftl", null, null, false); + assertEquals("utf-16", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + assertEquals(Locale.GERMANY, t.getLocale()); + assertEquals("Y,N", t.getBooleanFormat()); + } + { + Template t = cfg.getTemplate("default.ftl", null, null, false); + assertEquals("iso-8859-1", t.getEncoding()); + assertEquals(TEXT_WITH_ACCENTS, getTemplateOutput(t)); + } + } + + @Test + public void testConfigurableSettings() throws Exception { + Configuration cfg = new Configuration(Configuration.VERSION_3_0_0); + cfg.setLocale(Locale.US); + + TemplateConfiguration tcFR = new TemplateConfiguration(); + tcFR.setLocale(Locale.FRANCE); + TemplateConfiguration tcYN = new TemplateConfiguration(); + tcYN.setBooleanFormat("Y,N"); + TemplateConfiguration tc00 = new TemplateConfiguration(); + tc00.setNumberFormat("0.00"); + cfg.setTemplateConfigurations( + new MergingTemplateConfigurationFactory( + new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*(fr)*"), tcFR), + new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*(yn)*"), tcYN), + new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*(00)*"), tc00) + ) + ); + + String commonFTL = "${.locale} ${true?string} ${1.2}"; + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate("default", commonFTL); + tl.putTemplate("(fr)", commonFTL); + tl.putTemplate("(yn)(00)", commonFTL); + tl.putTemplate("(00)(fr)", commonFTL); + cfg.setTemplateLoader(tl); + + assertEquals("en_US true 1.2", getTemplateOutput(cfg.getTemplate("default"))); + assertEquals("fr_FR true 1,2", getTemplateOutput(cfg.getTemplate("(fr)"))); + assertEquals("en_US Y 1.20", getTemplateOutput(cfg.getTemplate("(yn)(00)"))); + assertEquals("fr_FR true 1,20", getTemplateOutput(cfg.getTemplate("(00)(fr)"))); + } + + @Test + public void testCustomAttributes() throws Exception { + Configuration cfg = new Configuration(Configuration.VERSION_3_0_0); + + TemplateConfiguration tc1 = new TemplateConfiguration(); + tc1.setCustomAttribute("a1", "a1tc1"); + tc1.setCustomAttribute("a2", "a2tc1"); + tc1.setCustomAttribute("a3", "a3tc1"); + CUST_ATT_1.set("ca1tc1", tc1); + CUST_ATT_2.set("ca2tc1", tc1); + + TemplateConfiguration tc2 = new TemplateConfiguration(); + tc2.setCustomAttribute("a1", "a1tc2"); + CUST_ATT_1.set("ca1tc2", tc2); + + cfg.setTemplateConfigurations( + new MergingTemplateConfigurationFactory( + new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*(tc1)*"), tc1), + new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*(tc2)*"), tc2) + ) + ); + + String commonFTL = "<#ftl attributes={ 'a3': 'a3temp' }>"; + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate("(tc1)", commonFTL); + tl.putTemplate("(tc1)noHeader", ""); + tl.putTemplate("(tc2)", commonFTL); + tl.putTemplate("(tc1)(tc2)", commonFTL); + cfg.setTemplateLoader(tl); + + { + Template t = cfg.getTemplate("(tc1)"); + assertEquals("a1tc1", t.getCustomAttribute("a1")); + assertEquals("a2tc1", t.getCustomAttribute("a2")); + assertEquals("a3temp", t.getCustomAttribute("a3")); + assertEquals("ca1tc1", CUST_ATT_1.get(t)); + assertEquals("ca2tc1", CUST_ATT_2.get(t)); + } + { + Template t = cfg.getTemplate("(tc1)noHeader"); + assertEquals("a1tc1", t.getCustomAttribute("a1")); + assertEquals("a2tc1", t.getCustomAttribute("a2")); + assertEquals("a3tc1", t.getCustomAttribute("a3")); + assertEquals("ca1tc1", CUST_ATT_1.get(t)); + assertEquals("ca2tc1", CUST_ATT_2.get(t)); + } + { + Template t = cfg.getTemplate("(tc2)"); + assertEquals("a1tc2", t.getCustomAttribute("a1")); + assertNull(t.getCustomAttribute("a2")); + assertEquals("a3temp", t.getCustomAttribute("a3")); + assertEquals("ca1tc2", CUST_ATT_1.get(t)); + assertNull(CUST_ATT_2.get(t)); + } + { + Template t = cfg.getTemplate("(tc1)(tc2)"); + assertEquals("a1tc2", t.getCustomAttribute("a1")); + assertEquals("a2tc1", t.getCustomAttribute("a2")); + assertEquals("a3temp", t.getCustomAttribute("a3")); + assertEquals("ca1tc2", CUST_ATT_1.get(t)); + assertEquals("ca2tc1", CUST_ATT_2.get(t)); + } + } + + private String getTemplateOutput(Template t) throws TemplateException, IOException { + StringWriter sw = new StringWriter(); + t.process(null, sw); + return sw.toString(); + } + + private Configuration createCommonEncodingTesterConfig() throws UnsupportedEncodingException { + Configuration cfg = new Configuration(Configuration.VERSION_3_0_0); + cfg.setDefaultEncoding("iso-8859-1"); + cfg.setLocale(Locale.US); + + ByteArrayTemplateLoader tl = new ByteArrayTemplateLoader(); + tl.putTemplate("utf8.ftl", TEXT_WITH_ACCENTS.getBytes("utf-8")); + tl.putTemplate("utf16.ftl", TEXT_WITH_ACCENTS.getBytes("utf-16")); + tl.putTemplate("default.ftl", TEXT_WITH_ACCENTS.getBytes("iso-8859-2")); + tl.putTemplate("utf8-latin2.ftl", + ("<#ftl encoding='iso-8859-2'>" + TEXT_WITH_ACCENTS).getBytes("iso-8859-2")); + tl.putTemplate("default-latin2.ftl", + ("<#ftl encoding='iso-8859-2'>" + TEXT_WITH_ACCENTS).getBytes("iso-8859-2")); + cfg.setTemplateLoader(tl); + + TemplateConfiguration tcUtf8 = new TemplateConfiguration(); + tcUtf8.setEncoding("utf-8"); + TemplateConfiguration tcUtf16 = new TemplateConfiguration(); + tcUtf16.setEncoding("utf-16"); + cfg.setTemplateConfigurations( + new FirstMatchTemplateConfigurationFactory( + new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*utf8*"), tcUtf8), + new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*utf16*"), tcUtf16) + ).allowNoMatch(true)); + return cfg; + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/TemplateDummyOutputModel.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateDummyOutputModel.java b/src/test/java/org/apache/freemarker/core/TemplateDummyOutputModel.java new file mode 100644 index 0000000..a4b19d1 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/TemplateDummyOutputModel.java @@ -0,0 +1,34 @@ +/* + * 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 org.apache.freemarker.core; + +import org.apache.freemarker.core.CommonTemplateMarkupOutputModel; + +public class TemplateDummyOutputModel extends CommonTemplateMarkupOutputModel<TemplateDummyOutputModel> { + + TemplateDummyOutputModel(String plainTextContent, String markupContet) { + super(plainTextContent, markupContet); + } + + @Override + public DummyOutputFormat getOutputFormat() { + return DummyOutputFormat.INSTANCE; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/TemplateLevelSettings.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateLevelSettings.java b/src/test/java/org/apache/freemarker/core/TemplateLevelSettings.java new file mode 100644 index 0000000..e6beba7 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/TemplateLevelSettings.java @@ -0,0 +1,107 @@ +/* + * 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 org.apache.freemarker.core; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.io.StringWriter; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.Template; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.Version; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StrongCacheStorage; +import org.junit.Test; + +public class TemplateLevelSettings { + + private static final String IMPORTED_FTL = "imported.ftl"; + private static final String INCLUDED_FTL = "included.ftl"; + private static final String MAIN_FTL = "main.ftl"; + private static final StringTemplateLoader TEMPLATES = new StringTemplateLoader(); + static { + TEMPLATES.putTemplate(MAIN_FTL, + "${true}<#include '" + INCLUDED_FTL + "'>" + + "${true}<#import '" + IMPORTED_FTL + "' as ns>" + + "${true}<@ns.impM1>${true}</@>" + + "${true}<@incM>${true}</@>" + + "${true}"); + TEMPLATES.putTemplate(INCLUDED_FTL, + "[inc:${true}]" + + "<#macro incM>[incM:${true}{<#nested>}${true}]</#macro>"); + TEMPLATES.putTemplate(IMPORTED_FTL, + "<#macro impM1>[impM1:${true}{<#nested>}${true}<@impM2>${true}</@>${true}]</#macro>" + + "<#macro impM2>[impM2:${true}{<#nested>}${true}]</#macro>" + ); + } + + @Test + public void test() throws IOException, TemplateException { + assertOutputs( + "M[inc:M]MM[impM1:M{M}M[impM2:M{M}M]M]M[incM:M{M}M]M", + "M,m", "INC,inc", "IMP,imp"); + assertOutputs( + "C[inc:C]CC[impM1:C{C}C[impM2:C{C}C]C]C[incM:C{C}C]C", + null, "INC,inc", "IMP,imp"); + assertOutputs( + "M[inc:M]MM[impM1:M{M}M[impM2:M{M}M]M]M[incM:M{M}M]M", + "M,m", null, "IMP,imp"); + assertOutputs( + "M[inc:M]MM[impM1:M{M}M[impM2:M{M}M]M]M[incM:M{M}M]M", + "M,m", "INC,inc", null); + } + + private void assertOutputs( + String expectedOutput, + String mainBoolFmt, String incBoolFmt, String impBoolFtm) + throws IOException, TemplateException { + assertEquals( + expectedOutput, + renderWith(Configuration.VERSION_3_0_0, mainBoolFmt, incBoolFmt, impBoolFtm)); + } + + private String renderWith(Version version, String mainBoolFmt, String incBoolFmt, String impBoolFtm) + throws IOException, TemplateException { + Configuration cfg = new Configuration(version); + cfg.setTemplateLoader(TEMPLATES); + cfg.setCacheStorage(new StrongCacheStorage()); + cfg.setBooleanFormat("C,c"); + + if (incBoolFmt != null) { + cfg.getTemplate(INCLUDED_FTL).setBooleanFormat(incBoolFmt); + } + + if (impBoolFtm != null) { + cfg.getTemplate(IMPORTED_FTL).setBooleanFormat(impBoolFtm); + } + + Template t = cfg.getTemplate(MAIN_FTL); + if (mainBoolFmt != null) { + t.setBooleanFormat(mainBoolFmt); + } + + StringWriter sw = new StringWriter(); + t.process(null, sw); + return sw.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java b/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java index f8d9c17..a05b5f8 100644 --- a/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java +++ b/src/test/java/org/apache/freemarker/core/TemplateLookupStrategyTest.java @@ -29,7 +29,6 @@ import java.io.IOException; import java.io.StringWriter; import java.util.Locale; -import org.apache.freemarker.core.ast.ParseException; import org.apache.freemarker.core.templateresolver.TemplateLookupContext; import org.apache.freemarker.core.templateresolver.TemplateLookupResult; import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/TemplateNameSpecialVariablesTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateNameSpecialVariablesTest.java b/src/test/java/org/apache/freemarker/core/TemplateNameSpecialVariablesTest.java new file mode 100644 index 0000000..13f075f --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/TemplateNameSpecialVariablesTest.java @@ -0,0 +1,169 @@ +/* + * 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 org.apache.freemarker.core; + +import java.io.IOException; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.Template; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.StringTemplateLoader; +import org.apache.freemarker.test.TemplateTest; +import org.junit.Before; +import org.junit.Test; + +public class TemplateNameSpecialVariablesTest extends TemplateTest { + + private static TemplateLoader createTemplateLoader(String specVar) { + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate("main.ftl", + "In main: ${" + specVar + "}\n" + + "<#import 'imp.ftl' as i>" + + "In imp: ${inImp}\n" + + "In main: ${" + specVar + "}\n" + + "<@i.impM>${" + specVar + "}</@>\n" + + "<@i.impM2 />\n" + + "In main: ${" + specVar + "}\n" + + "<#include 'inc.ftl'>" + + "In main: ${" + specVar + "}\n" + + "<@incM>${" + specVar + "}</@>\n" + + "<@incM2 />\n" + + "In main: ${" + specVar + "}\n" + ); + tl.putTemplate("imp.ftl", + "<#global inImp = " + specVar + ">" + + "<#macro impM>" + + "${" + specVar + "}\n" + + "{<#nested>}" + + "</#macro>" + + "<#macro impM2>" + + "In imp call imp:\n" + + "<@impM>${" + specVar + "}</@>\n" + + "After: ${" + specVar + "}" + + "</#macro>" + ); + tl.putTemplate("inc.ftl", + "In inc: ${" + specVar + "}\n" + + "In inc call imp:\n" + + "<@i.impM>${" + specVar + "}</@>\n" + + "<#macro incM>" + + "${" + specVar + "}\n" + + "{<#nested>}" + + "</#macro>" + + "<#macro incM2>" + + "In inc call imp:\n" + + "<@i.impM>${" + specVar + "}</@>" + + "</#macro>" + ); + return tl; + } + + private static final String PRINT_ALL_FTL + = "ct=${.currentTemplateName!'-'}, mt=${.mainTemplateName!'-'}"; + + @Test + public void testMainTemplateName() throws IOException, TemplateException { + getConfiguration().setTemplateLoader(createTemplateLoader(".mainTemplateName")); + assertOutputForNamed("main.ftl", + "In main: main.ftl\n" + + "In imp: main.ftl\n" + + "In main: main.ftl\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "In imp call imp:\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "After: main.ftl\n" + + "In main: main.ftl\n" + + "In inc: main.ftl\n" + + "In inc call imp:\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "In main: main.ftl\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "In inc call imp:\n" + + "main.ftl\n" + + "{main.ftl}\n" + + "In main: main.ftl\n"); + } + + @Test + public void testCurrentTemplateName() throws IOException, TemplateException { + getConfiguration().setTemplateLoader(createTemplateLoader(".currentTemplateName")); + assertOutputForNamed("main.ftl", + "In main: main.ftl\n" + + "In imp: imp.ftl\n" + + "In main: main.ftl\n" + + "imp.ftl\n" + + "{main.ftl}\n" + + "In imp call imp:\n" + + "imp.ftl\n" + + "{imp.ftl}\n" + + "After: imp.ftl\n" + + "In main: main.ftl\n" + + "In inc: inc.ftl\n" + + "In inc call imp:\n" + + "imp.ftl\n" + + "{inc.ftl}\n" + + "In main: main.ftl\n" + + "inc.ftl\n" + + "{main.ftl}\n" + + "In inc call imp:\n" + + "imp.ftl\n" + + "{inc.ftl}\n" + + "In main: main.ftl\n"); + } + + @Before + public void setup() { + Configuration cfg = getConfiguration(); + cfg.setWhitespaceStripping(false); + } + + @Test + public void testInAdhocTemplate() throws TemplateException, IOException { + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate("inc.ftl", "Inc: " + PRINT_ALL_FTL); + getConfiguration().setTemplateLoader(tl); + + // In nameless templates, the deprecated .templateName is "", but the new variables are missing values. + assertOutput(new Template(null, PRINT_ALL_FTL + "; <#include 'inc.ftl'>", getConfiguration()), + "ct=-, mt=-; Inc: ct=inc.ftl, mt=-"); + + assertOutput(new Template("foo.ftl", PRINT_ALL_FTL + "; <#include 'inc.ftl'>", getConfiguration()), + "ct=foo.ftl, mt=foo.ftl; Inc: ct=inc.ftl, mt=foo.ftl"); + } + + @Test + public void testInInterpretTemplate() throws TemplateException, IOException { + getConfiguration().setSharedVariable("t", PRINT_ALL_FTL); + assertOutput(new Template("foo.ftl", PRINT_ALL_FTL + "; <@t?interpret />", getConfiguration()), + "ct=foo.ftl, mt=foo.ftl; " + + "ct=foo.ftl->anonymous_interpreted, mt=foo.ftl"); + assertOutput(new Template(null, PRINT_ALL_FTL + "; <@t?interpret />", getConfiguration()), + "ct=-, mt=-; " + + "ct=nameless_template->anonymous_interpreted, mt=-"); + assertOutput(new Template("foo.ftl", PRINT_ALL_FTL + "; <@[t,'bar']?interpret />", getConfiguration()), + "ct=foo.ftl, mt=foo.ftl; " + + "ct=foo.ftl->bar, mt=foo.ftl"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/TemplateSeldomEscapedOutputModel.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TemplateSeldomEscapedOutputModel.java b/src/test/java/org/apache/freemarker/core/TemplateSeldomEscapedOutputModel.java new file mode 100644 index 0000000..76afdd2 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/TemplateSeldomEscapedOutputModel.java @@ -0,0 +1,34 @@ +/* + * 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 org.apache.freemarker.core; + +import org.apache.freemarker.core.CommonTemplateMarkupOutputModel; + +public class TemplateSeldomEscapedOutputModel extends CommonTemplateMarkupOutputModel<TemplateSeldomEscapedOutputModel> { + + TemplateSeldomEscapedOutputModel(String plainTextContent, String markupContet) { + super(plainTextContent, markupContet); + } + + @Override + public SeldomEscapedOutputFormat getOutputFormat() { + return SeldomEscapedOutputFormat.INSTANCE; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java b/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java new file mode 100644 index 0000000..a4bfb29 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/TheadInterruptingSupportTest.java @@ -0,0 +1,166 @@ +/* + * 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 org.apache.freemarker.core; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.Map; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.Template; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core._CoreAPI; +import org.apache.freemarker.core.ThreadInterruptionSupportTemplatePostProcessor.TemplateProcessingThreadInterruptedException; +import org.apache.freemarker.core.model.TemplateDirectiveBody; +import org.apache.freemarker.core.model.TemplateDirectiveModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.util._NullWriter; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TheadInterruptingSupportTest { + + private static final Logger LOG = LoggerFactory.getLogger(TheadInterruptingSupportTest.class); + + private static final int TEMPLATE_INTERRUPTION_TIMEOUT = 5000; + private final Configuration cfg = new Configuration(Configuration.VERSION_3_0_0); + + @Test + public void test() throws IOException, InterruptedException { + assertCanBeInterrupted("<#list 1.. as x></#list>"); + assertCanBeInterrupted("<#list 1.. as x>${x}</#list>"); + assertCanBeInterrupted("<#list 1.. as x>t${x}</#list>"); + assertCanBeInterrupted("<#list 1.. as x><#list 1.. as y>${y}</#list></#list>"); + assertCanBeInterrupted("<#list 1.. as x>${x}<#else>nope</#list>"); + assertCanBeInterrupted("<#list 1..>[<#items as x>${x}</#items>]<#else>nope</#list>"); + assertCanBeInterrupted("<@customLoopDirective />"); + assertCanBeInterrupted("<@customLoopDirective>x</@>"); + assertCanBeInterrupted("<@customLoopDirective><#if true>x</#if></@>"); + assertCanBeInterrupted("<#macro selfCalling><@sleepDirective/><@selfCalling /></#macro><@selfCalling />"); + assertCanBeInterrupted("<#function selfCalling><@sleepDirective/>${selfCalling()}</#function>${selfCalling()}"); + assertCanBeInterrupted("<#list 1.. as _><#attempt><@sleepDirective/><#recover>suppress</#attempt></#list>"); + assertCanBeInterrupted("<#attempt><#list 1.. as _></#list><#recover>suppress</#attempt>"); + } + + private void assertCanBeInterrupted(final String templateSourceCode) throws IOException, InterruptedException { + TemplateRunnerThread trt = new TemplateRunnerThread(templateSourceCode); + trt.start(); + synchronized (trt) { + while (!trt.isStarted()) { + trt.wait(); + } + } + Thread.sleep(50); // Just to ensure (hope...) that the template execution reaches "deep" enough + trt.interrupt(); + trt.join(TEMPLATE_INTERRUPTION_TIMEOUT); + assertTrue(trt.isTemplateProcessingInterrupted()); + } + + public class TemplateRunnerThread extends Thread { + + private final Template template; + private boolean started; + private boolean templateProcessingInterrupted; + + public TemplateRunnerThread(String templateSourceCode) throws IOException { + template = new Template(null, "<@startedDirective/>" + templateSourceCode, cfg); + _CoreAPI.addThreadInterruptedChecks(template); + } + + @Override + public void run() { + try { + template.process(this, _NullWriter.INSTANCE); + } catch (TemplateProcessingThreadInterruptedException e) { + //LOG.debug("Template processing interrupted", e); + synchronized (this) { + templateProcessingInterrupted = true; + } + } catch (Throwable e) { + LOG.error("Template processing failed", e); + } + } + + public synchronized boolean isTemplateProcessingInterrupted() { + return templateProcessingInterrupted; + } + + public synchronized boolean isStarted() { + return started; + } + + public TemplateDirectiveModel getStartedDirective() { + return new StartedDirective(); + } + + public TemplateDirectiveModel getCustomLoopDirective() { + return new CustomLoopDirective(); + } + + public TemplateDirectiveModel getSleepDirective() { + return new SleepDirective(); + } + + public class StartedDirective implements TemplateDirectiveModel { + + @Override + public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + throws TemplateException, IOException { + synchronized (TemplateRunnerThread.this) { + started = true; + TemplateRunnerThread.this.notifyAll(); + } + } + + } + + public class CustomLoopDirective implements TemplateDirectiveModel { + + @Override + public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + throws TemplateException, IOException { + while (true) { + body.render(_NullWriter.INSTANCE); + } + } + + } + + public class SleepDirective implements TemplateDirectiveModel { + + @Override + public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) + throws TemplateException, IOException { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // Thread.sleep has reset the interrupted flag (because it has thrown InterruptedException). + Thread.currentThread().interrupt(); + } + } + + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java b/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java new file mode 100644 index 0000000..1b8fdf8 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java @@ -0,0 +1,105 @@ +/* + * 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 org.apache.freemarker.core; + +import java.io.StringReader; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.freemarker.test.TemplateTest; +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +public class TypeErrorMessagesTest extends TemplateTest { + + static final Document doc; + static { + try { + DocumentBuilder docBuilder; + docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + doc = docBuilder.parse(new InputSource(new StringReader( + "<a><b>123</b><c a='true'>1</c><c a='false'>2</c></a>"))); + } catch (Exception e) { + throw new RuntimeException("Failed to build data-model", e); + } + } + + @Test + public void testNumericalBinaryOperator() { + assertErrorContains("${n - s}", "\"-\"", "right-hand", "number", "string"); + assertErrorContains("${s - n}", "\"-\"", "left-hand", "number", "string"); + } + + @Test + public void testGetterMistake() { + assertErrorContains("${bean.getX}", "${...}", + "number", "string", "method", "obj.getSomething", "obj.something"); + assertErrorContains("${1 * bean.getX}", "right-hand", + "number", "\\!string", "method", "obj.getSomething", "obj.something"); + assertErrorContains("<#if bean.isB></#if>", "condition", + "boolean", "method", "obj.isSomething", "obj.something"); + assertErrorContains("<#if bean.isB></#if>", "condition", + "boolean", "method", "obj.isSomething", "obj.something"); + assertErrorContains("${bean.voidM}", + "string", "method", "\\!()"); + assertErrorContains("${bean.intM}", + "string", "method", "obj.something()"); + assertErrorContains("${bean.intMP}", + "string", "method", "obj.something(params)"); + } + + @Test + public void testXMLTypeMismarches() throws Exception { + assertErrorContains("${doc.a.c}", + "used as string", "query result", "2", "multiple matches"); + assertErrorContains("${doc.a.c?boolean}", + "used as string", "query result", "2", "multiple matches"); + assertErrorContains("${doc.a.d}", + "used as string", "query result", "0", "no matches"); + assertErrorContains("${doc.a.d?boolean}", + "used as string", "query result", "0", "no matches"); + + assertErrorContains("${doc.a.c.@a}", + "used as string", "query result", "2", "multiple matches"); + assertErrorContains("${doc.a.d.@b}", + "used as string", "query result", "x", "no matches"); + + assertErrorContains("${doc.a.b * 2}", + "used as number", "text", "explicit conversion"); + assertErrorContains("<#if doc.a.b></#if>", + "used as number", "text", "explicit conversion"); + + assertErrorContains("${doc.a.d?nodeName}", + "used as node", "query result", "0", "no matches"); + assertErrorContains("${doc.a.c?nodeName}", + "used as node", "query result", "2", "multiple matches"); + } + + @Override + protected Object createDataModel() { + Map<String, Object> dataModel = createCommonTestValuesDataModel(); + dataModel.put("doc", doc); + return dataModel; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/UnclosedCommentTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/UnclosedCommentTest.java b/src/test/java/org/apache/freemarker/core/UnclosedCommentTest.java new file mode 100644 index 0000000..9e532bd --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/UnclosedCommentTest.java @@ -0,0 +1,44 @@ +/* + * 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 org.apache.freemarker.core; + +import java.io.IOException; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.test.TemplateTest; +import org.junit.Test; + +public class UnclosedCommentTest extends TemplateTest { + + @Test + public void test() throws IOException, TemplateException { + setConfiguration(new Configuration(Configuration.VERSION_3_0_0)); + assertErrorContains("foo<#--", "end of file"); // Not too good... + assertErrorContains("foo<#-- ", "Unclosed", "<#--"); + assertErrorContains("foo<#--bar", "Unclosed", "<#--"); + assertErrorContains("foo\n<#--\n", "Unclosed", "<#--"); + assertErrorContains("foo<#noparse>", "end of file"); // Not too good... + assertErrorContains("foo<#noparse> ", "Unclosed", "#noparse"); + assertErrorContains("foo<#noparse>bar", "Unclosed", "#noparse"); + assertErrorContains("foo\n<#noparse>\n", "Unclosed", "#noparse"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/WhitespaceStrippingTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/WhitespaceStrippingTest.java b/src/test/java/org/apache/freemarker/core/WhitespaceStrippingTest.java new file mode 100644 index 0000000..06f1805 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/WhitespaceStrippingTest.java @@ -0,0 +1,71 @@ +/* + * 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 org.apache.freemarker.core; + +import java.io.IOException; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.test.TemplateTest; +import org.junit.Test; + +public class WhitespaceStrippingTest extends TemplateTest { + + private final Configuration cfgStripWS = new Configuration(Configuration.VERSION_3_0_0); + + private final Configuration cfgNoStripWS = new Configuration(Configuration.VERSION_3_0_0); + { + cfgNoStripWS.setWhitespaceStripping(false); + } + + @Test + public void testBasics() throws Exception { + assertOutput("<#assign x = 1>\n<#assign y = 2>\n${x}\n${y}", "1\n2", "\n\n1\n2"); + assertOutput(" <#assign x = 1> \n <#assign y = 2> \n${x}\n${y}", "1\n2", " \n \n1\n2"); + } + + @Test + public void testFTLHeader() throws Exception { + assertOutput("<#ftl>x", "x", "x"); + assertOutput(" <#ftl> x", " x", " x"); + assertOutput("\n<#ftl>\nx", "x", "x"); + assertOutput("\n<#ftl>\t \nx", "x", "x"); + assertOutput(" \n \n <#ftl> \n \n x", " \n x", " \n x"); + } + + @Test + public void testComment() throws Exception { + assertOutput(" a <#-- --> b ", " a b ", " a b "); + assertOutput(" a \n<#-- -->\n b ", " a \n b ", " a \n\n b "); + // These are wrong, but needed for 2.3.0 compatibility: + assertOutput(" a \n <#-- --> \n b ", " a \n b ", " a \n \n b "); + assertOutput(" a \n\t<#-- --> \n b ", " a \n\t b ", " a \n\t \n b "); + } + + private void assertOutput(String ftl, String expectedOutStripped, String expectedOutNonStripped) + throws IOException, TemplateException { + setConfiguration(cfgStripWS); + assertOutput(ftl, expectedOutStripped); + + setConfiguration(cfgNoStripWS); + assertOutput(ftl, expectedOutNonStripped); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/XHTMLOutputFormatTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/XHTMLOutputFormatTest.java b/src/test/java/org/apache/freemarker/core/XHTMLOutputFormatTest.java new file mode 100644 index 0000000..d4d9ca0 --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/XHTMLOutputFormatTest.java @@ -0,0 +1,59 @@ +/* + * 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 org.apache.freemarker.core; + +import static org.apache.freemarker.core.XHTMLOutputFormat.*; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.StringWriter; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.junit.Test; + +public class XHTMLOutputFormatTest { + + @Test + public void testOutputMO() throws TemplateModelException, IOException { + StringWriter out = new StringWriter(); + INSTANCE.output(INSTANCE.fromPlainTextByEscaping("a'b"), out); + assertEquals("a'b", out.toString()); + } + + @Test + public void testOutputString() throws TemplateModelException, IOException { + StringWriter out = new StringWriter(); + INSTANCE.output("a'b", out); + assertEquals("a'b", out.toString()); + } + + @Test + public void testEscaplePlainText() { + assertEquals("", INSTANCE.escapePlainText("")); + assertEquals("a", INSTANCE.escapePlainText("a")); + assertEquals("<a&b'c"d>", INSTANCE.escapePlainText("<a&b'c\"d>")); + assertEquals("<>", INSTANCE.escapePlainText("<>")); + } + + @Test + public void testGetMimeType() { + assertEquals("application/xhtml+xml", INSTANCE.getMimeType()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/XMLOutputFormatTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/XMLOutputFormatTest.java b/src/test/java/org/apache/freemarker/core/XMLOutputFormatTest.java new file mode 100644 index 0000000..09c075f --- /dev/null +++ b/src/test/java/org/apache/freemarker/core/XMLOutputFormatTest.java @@ -0,0 +1,59 @@ +/* + * 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 org.apache.freemarker.core; + +import static org.apache.freemarker.core.XMLOutputFormat.*; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.StringWriter; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.junit.Test; + +public class XMLOutputFormatTest { + + @Test + public void testOutputMO() throws TemplateModelException, IOException { + StringWriter out = new StringWriter(); + INSTANCE.output(INSTANCE.fromPlainTextByEscaping("a'b"), out); + assertEquals("a'b", out.toString()); + } + + @Test + public void testOutputString() throws TemplateModelException, IOException { + StringWriter out = new StringWriter(); + INSTANCE.output("a'b", out); + assertEquals("a'b", out.toString()); + } + + @Test + public void testEscaplePlainText() { + assertEquals("", INSTANCE.escapePlainText("")); + assertEquals("a", INSTANCE.escapePlainText("a")); + assertEquals("<a&b'c"d>", INSTANCE.escapePlainText("<a&b'c\"d>")); + assertEquals("<>", INSTANCE.escapePlainText("<>")); + } + + @Test + public void testGetMimeType() { + assertEquals("application/xml", INSTANCE.getMimeType()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/ast/ASTBasedErrorMessagesTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/ast/ASTBasedErrorMessagesTest.java b/src/test/java/org/apache/freemarker/core/ast/ASTBasedErrorMessagesTest.java deleted file mode 100644 index 62aadb4..0000000 --- a/src/test/java/org/apache/freemarker/core/ast/ASTBasedErrorMessagesTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 org.apache.freemarker.core.ast; - -import java.util.Map; - -import org.apache.freemarker.test.TemplateTest; -import org.junit.Test; - -public class ASTBasedErrorMessagesTest extends TemplateTest { - - @Test - public void testInvalidRefBasic() { - assertErrorContains("${foo}", "foo", "specify a default"); - assertErrorContains("${map[foo]}", "foo", "\\!map[", "specify a default"); - } - - @Test - public void testInvalidRefDollar() { - assertErrorContains("${$x}", "$x", "must not start with \"$\"", "specify a default"); - assertErrorContains("${map.$x}", "map.$x", "must not start with \"$\"", "specify a default"); - } - - @Test - public void testInvalidRefAfterDot() { - assertErrorContains("${map.foo.bar}", "map.foo", "\\!foo.bar", "after the last dot", "specify a default"); - } - - @Test - public void testInvalidRefInSquareBrackets() { - assertErrorContains("${map['foo']}", "map", "final [] step", "specify a default"); - } - - @Test - public void testInvalidRefSize() { - assertErrorContains("${map.size()}", "map.size", "?size", "specify a default"); - assertErrorContains("${map.length()}", "map.length", "?length", "specify a default"); - } - - @Override - protected Object createDataModel() { - Map<String, Object> dataModel = createCommonTestValuesDataModel(); - dataModel.put("overloads", new Overloads()); - return dataModel; - } - - public static class Overloads { - - @SuppressWarnings("unused") - public void m(String s) {} - - @SuppressWarnings("unused") - public void m(int i) {} - - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/ast/ASTPrinter.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/ast/ASTPrinter.java b/src/test/java/org/apache/freemarker/core/ast/ASTPrinter.java deleted file mode 100644 index 73fc4a4..0000000 --- a/src/test/java/org/apache/freemarker/core/ast/ASTPrinter.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * 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 org.apache.freemarker.core.ast; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; -import java.nio.ByteBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.Charset; -import java.nio.charset.CodingErrorAction; -import java.util.Enumeration; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -import org.apache.freemarker.core.Configuration; -import org.apache.freemarker.core.Template; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.util.FTLUtil; -import org.apache.freemarker.core.util._ClassUtil; -import org.apache.freemarker.core.util._StringUtil; - -/** - * Static methods and command-line tool for printing the AST of a template. - */ -public class ASTPrinter { - - private final Configuration cfg; - private int successfulCounter; - private int failedCounter; - - static public void main(String[] args) throws IOException { - if (args.length == 0) { - usage(); - System.exit(-1); - } - - ASTPrinter astp = new ASTPrinter(); - if (args[0].equalsIgnoreCase("-r")) { - astp.mainRecursive(args); - } else { - astp.mainSingleTemplate(args); - } - } - - private ASTPrinter() { - cfg = new Configuration(Configuration.VERSION_3_0_0); - } - - private void mainSingleTemplate(String[] args) throws IOException, FileNotFoundException { - final String templateFileName; - final String templateContent; - if (args[0].startsWith("ftl:")) { - templateFileName = null; - templateContent = args[0]; - } else { - templateFileName = args[0]; - templateContent = null; - } - - Template t = new Template( - templateFileName, - templateFileName == null ? new StringReader(templateContent) : new FileReader(templateFileName), - cfg); - - p(getASTAsString(t)); - } - - private void mainRecursive(String[] args) throws IOException { - if (args.length != 4) { - p("Number of arguments must be 4, but was: " + args.length); - usage(); - System.exit(-1); - } - - final String srcDirPath = args[1].trim(); - File srcDir = new File(srcDirPath); - if (!srcDir.isDirectory()) { - p("This should be an existing directory: " + srcDirPath); - System.exit(-1); - } - - Pattern fnPattern; - try { - fnPattern = Pattern.compile(args[2]); - } catch (PatternSyntaxException e) { - p(_StringUtil.jQuote(args[2]) + " is not a valid regular expression"); - System.exit(-1); - return; - } - - final String dstDirPath = args[3].trim(); - File dstDir = new File(dstDirPath); - if (!dstDir.isDirectory()) { - p("This should be an existing directory: " + dstDirPath); - System.exit(-1); - } - - long startTime = System.currentTimeMillis(); - recurse(srcDir, fnPattern, dstDir); - long endTime = System.currentTimeMillis(); - - p("Templates successfully processed " + successfulCounter + ", failed " + failedCounter - + ". Time taken: " + (endTime - startTime) / 1000.0 + " s"); - } - - private void recurse(File srcDir, Pattern fnPattern, File dstDir) throws IOException { - File[] files = srcDir.listFiles(); - if (files == null) { - throw new IOException("Failed to kust directory: " + srcDir); - } - for (File file : files) { - if (file.isDirectory()) { - recurse(file, fnPattern, new File(dstDir, file.getName())); - } else { - if (fnPattern.matcher(file.getName()).matches()) { - File dstFile = new File(dstDir, file.getName()); - String res; - try { - Template t = new Template(file.getPath().replace('\\', '/'), loadIntoString(file), cfg); - res = getASTAsString(t); - successfulCounter++; - } catch (ParseException e) { - res = "<<<FAILED>>>\n" + e.getMessage(); - failedCounter++; - p(""); - p("-------------------------failed-------------------------"); - p("Error message was saved into: " + dstFile.getAbsolutePath()); - p(""); - p(e.getMessage()); - } - save(res, dstFile); - } - } - } - } - - private String loadIntoString(File file) throws IOException { - long ln = file.length(); - if (ln < 0) { - throw new IOException("Failed to get the length of " + file); - } - byte[] buffer = new byte[(int) ln]; - InputStream in = new FileInputStream(file); - try { - int offset = 0; - int bytesRead; - while (offset < buffer.length) { - bytesRead = in.read(buffer, offset, buffer.length - offset); - if (bytesRead == -1) { - throw new IOException("Unexpected end of file: " + file); - } - offset += bytesRead; - } - } finally { - in.close(); - } - - try { - return decode(buffer, Charset.forName("UTF-8")); - } catch (CharacterCodingException e) { - return decode(buffer, Charset.forName("ISO-8859-1")); - } - } - - private String decode(byte[] buffer, Charset charset) throws CharacterCodingException { - return charset.newDecoder() - .onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT) - .decode(ByteBuffer.wrap(buffer)).toString(); - } - - private void save(String astStr, File file) throws IOException { - File parentDir = file.getParentFile(); - if (!parentDir.isDirectory() && !parentDir.mkdirs()) { - throw new IOException("Failed to create parent directory: " + parentDir); - } - - Writer w = new BufferedWriter(new FileWriter(file)); - try { - w.write(astStr); - } finally { - w.close(); - } - } - - private static void usage() { - p("Prints template Abstract Syntax Tree (AST) as plain text."); - p("Usage:"); - p(" java org.apache.freemarker.core.ast.PrintAST <templateFile>"); - p(" java org.apache.freemarker.core.ast.PrintAST ftl:<templateSource>"); - p(" java org.apache.freemarker.core.ast.PrintAST -r <src-directory> <regexp> <dst-directory>"); - } - - private static final String INDENTATION = " "; - - public static String getASTAsString(String ftl) throws IOException { - return getASTAsString(ftl, (Options) null); - } - - public static String getASTAsString(String ftl, Options opts) throws IOException { - return getASTAsString(null, ftl, opts); - } - - public static String getASTAsString(String templateName, String ftl) throws IOException { - return getASTAsString(templateName, ftl, null); - } - - public static String getASTAsString(String templateName, String ftl, Options opts) throws IOException { - Configuration cfg = new Configuration(); - Template t = new Template(templateName, ftl, cfg); - return getASTAsString(t, opts); - } - - public static String getASTAsString(Template t) throws IOException { - return getASTAsString(t, null); - } - - public static String getASTAsString(Template t, Options opts) throws IOException { - validateAST(t); - - StringWriter out = new StringWriter(); - printNode(t.getRootTreeNode(), "", null, opts != null ? opts : Options.DEFAULT_INSTANCE, out); - return out.toString(); - } - - public static void validateAST(Template t) throws InvalidASTException { - final TemplateElement node = t.getRootTreeNode(); - if (node.getParentElement() != null) { - throw new InvalidASTException("Root node parent must be null." - + "\nRoot node: " + node.dump(false) - + "\nParent" - + ": " + node.getParentElement().getClass() + ", " + node.getParentElement().dump(false)); - } - validateAST(node); - } - - private static void validateAST(TemplateElement te) { - int childCount = te.getChildCount(); - for (int i = 0; i < childCount; i++) { - TemplateElement child = te.getChild(i); - TemplateElement parentElement = child.getParentElement(); - // As MixedContent.accept does nothing but return its regulatedChildren, it's optimized out in the final - // AST tree. While it will be present as a child, the parent element also will have regularedChildren - // that contains the children of the MixedContent directly. - if (parentElement instanceof MixedContent && parentElement.getParentElement() != null) { - parentElement = parentElement.getParentElement(); - } - if (parentElement != te) { - throw new InvalidASTException("Wrong parent node." - + "\nNode: " + child.dump(false) - + "\nExpected parent: " + te.dump(false) - + "\nActual parent: " + parentElement.dump(false)); - } - if (child.getIndex() != i) { - throw new InvalidASTException("Wrong node index." - + "\nNode: " + child.dump(false) - + "\nExpected index: " + i - + "\nActual index: " + child.getIndex()); - } - } - if (te instanceof MixedContent && te.getChildCount() < 2) { - throw new InvalidASTException("Mixed content with child count less than 2 should removed by optimizatoin, " - + "but found one with " + te.getChildCount() + " child(ren)."); - } - TemplateElement[] regulatedChildren = te.getChildBuffer(); - if (regulatedChildren != null) { - if (childCount == 0) { - throw new InvalidASTException( - "regularChildren must be null when regularChild is 0." - + "\nNode: " + te.dump(false)); - } - for (int i = 0; i < te.getChildCount(); i++) { - if (regulatedChildren[i] == null) { - throw new InvalidASTException( - "regularChildren can't be null at index " + i - + "\nNode: " + te.dump(false)); - } - } - for (int i = te.getChildCount(); i < regulatedChildren.length; i++) { - if (regulatedChildren[i] != null) { - throw new InvalidASTException( - "regularChildren can't be non-null at index " + i - + "\nNode: " + te.dump(false)); - } - } - } else { - if (childCount != 0) { - throw new InvalidASTException( - "regularChildren mustn't be null when regularChild isn't 0." - + "\nNode: " + te.dump(false)); - } - } - } - - private static void printNode(Object node, String ind, ParameterRole paramRole, Options opts, Writer out) throws IOException { - if (node instanceof TemplateObject) { - TemplateObject tObj = (TemplateObject) node; - - printNodeLineStart(paramRole, ind, out); - out.write(tObj.getNodeTypeSymbol()); - printNodeLineEnd(node, out, opts); - - if (opts.getShowConstantValue() && node instanceof Expression) { - TemplateModel tm = ((Expression) node).constantValue; - if (tm != null) { - out.write(INDENTATION); - out.write(ind); - out.write("= const "); - out.write(FTLUtil.getTypeDescription(tm)); - out.write(' '); - out.write(tm.toString()); - out.write('\n'); - } - } - - int paramCnt = tObj.getParameterCount(); - for (int i = 0; i < paramCnt; i++) { - ParameterRole role = tObj.getParameterRole(i); - if (role == null) throw new NullPointerException("parameter role"); - Object value = tObj.getParameterValue(i); - printNode(value, ind + INDENTATION, role, opts, out); - } - if (tObj instanceof TemplateElement) { - Enumeration enu = ((TemplateElement) tObj).children(); - while (enu.hasMoreElements()) { - printNode(enu.nextElement(), INDENTATION + ind, null, opts, out); - } - } - } else { - printNodeLineStart(paramRole, ind, out); - out.write(_StringUtil.jQuote(node)); - printNodeLineEnd(node, out, opts); - } - } - - protected static void printNodeLineEnd(Object node, Writer out, Options opts) throws IOException { - boolean commentStared = false; - if (opts.getShowJavaClass()) { - out.write(" // "); - commentStared = true; - out.write(_ClassUtil.getShortClassNameOfObject(node, true)); - } - if (opts.getShowLocation() && node instanceof TemplateObject) { - if (!commentStared) { - out.write(" // "); - commentStared = true; - } else { - out.write("; "); - } - TemplateObject tObj = (TemplateObject) node; - out.write("Location " + tObj.beginLine + ":" + tObj.beginColumn + "-" + tObj.endLine + ":" + tObj.endColumn); - } - out.write('\n'); - } - - private static void printNodeLineStart(ParameterRole paramRole, String ind, Writer out) throws IOException { - out.write(ind); - if (paramRole != null) { - out.write("- "); - out.write(paramRole.toString()); - out.write(": "); - } - } - - public static class Options { - - private final static Options DEFAULT_INSTANCE = new Options(); - - private boolean showJavaClass = true; - private boolean showConstantValue = false; - private boolean showLocation = false; - - public boolean getShowJavaClass() { - return showJavaClass; - } - - public void setShowJavaClass(boolean showJavaClass) { - this.showJavaClass = showJavaClass; - } - - public boolean getShowConstantValue() { - return showConstantValue; - } - - public void setShowConstantValue(boolean showConstantValue) { - this.showConstantValue = showConstantValue; - } - - public boolean getShowLocation() { - return showLocation; - } - - public void setShowLocation(boolean showLocation) { - this.showLocation = showLocation; - } - - } - - private static void p(Object obj) { - System.out.println(obj); - } - - public static class InvalidASTException extends RuntimeException { - - public InvalidASTException(String message, Throwable cause) { - super(message, cause); - } - - public InvalidASTException(String message) { - super(message); - } - - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/ast/ASTTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/ast/ASTTest.java b/src/test/java/org/apache/freemarker/core/ast/ASTTest.java deleted file mode 100644 index 21e54e2..0000000 --- a/src/test/java/org/apache/freemarker/core/ast/ASTTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 org.apache.freemarker.core.ast; - -import java.io.FileNotFoundException; -import java.io.IOException; - -import org.apache.freemarker.core.ast.ASTPrinter.Options; -import org.apache.freemarker.core.util._StringUtil; -import org.apache.freemarker.test.util.FileTestCase; -import org.apache.freemarker.test.util.TestUtil; - -public class ASTTest extends FileTestCase { - - public ASTTest(String name) { - super(name); - } - - public void test1() throws Exception { - testAST("ast-1"); - } - - public void testRange() throws Exception { - testAST("ast-range"); - } - - public void testAssignments() throws Exception { - testAST("ast-assignments"); - } - - public void testBuiltins() throws Exception { - testAST("ast-builtins"); - } - - public void testStringLiteralInterpolation() throws Exception { - testAST("ast-strlitinterpolation"); - } - - public void testWhitespaceStripping() throws Exception { - testAST("ast-whitespacestripping"); - } - - public void testMixedContentSimplifications() throws Exception { - testAST("ast-mixedcontentsimplifications"); - } - - public void testMultipleIgnoredChildren() throws Exception { - testAST("ast-multipleignoredchildren"); - } - - public void testNestedIgnoredChildren() throws Exception { - testAST("ast-nestedignoredchildren"); - } - - public void testLocations() throws Exception { - testASTWithLocations("ast-locations"); - } - - private void testAST(String testName) throws FileNotFoundException, IOException { - testAST(testName, null); - } - - private void testASTWithLocations(String testName) throws FileNotFoundException, IOException { - Options options = new Options(); - options.setShowLocation(true); - testAST(testName, options); - } - - private void testAST(String testName, Options ops) throws FileNotFoundException, IOException { - final String templateName = testName + ".ftl"; - assertExpectedFileEqualsString( - testName + ".ast", - ASTPrinter.getASTAsString(templateName, - TestUtil.removeFTLCopyrightComment(normalizeLineBreaks(loadResource(templateName))), ops)); - } - - private String normalizeLineBreaks(final String s) throws FileNotFoundException, IOException { - return _StringUtil.replace(s, "\r\n", "\n").replace('\r', '\n'); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/ast/AppMetaTemplateDateFormatFactory.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/freemarker/core/ast/AppMetaTemplateDateFormatFactory.java b/src/test/java/org/apache/freemarker/core/ast/AppMetaTemplateDateFormatFactory.java deleted file mode 100644 index 3205181..0000000 --- a/src/test/java/org/apache/freemarker/core/ast/AppMetaTemplateDateFormatFactory.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 org.apache.freemarker.core.ast; - -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -import org.apache.freemarker.core.ast.Environment; -import org.apache.freemarker.core.ast.InvalidFormatParametersException; -import org.apache.freemarker.core.ast.TemplateDateFormat; -import org.apache.freemarker.core.ast.TemplateDateFormatFactory; -import org.apache.freemarker.core.ast.TemplateFormatUtil; -import org.apache.freemarker.core.ast.UnformattableValueException; -import org.apache.freemarker.core.ast.UnknownDateTypeFormattingUnsupportedException; -import org.apache.freemarker.core.ast.UnparsableValueException; -import org.apache.freemarker.core.model.TemplateDateModel; -import org.apache.freemarker.core.model.TemplateModelException; - -public class AppMetaTemplateDateFormatFactory extends TemplateDateFormatFactory { - - public static final AppMetaTemplateDateFormatFactory INSTANCE = new AppMetaTemplateDateFormatFactory(); - - private AppMetaTemplateDateFormatFactory() { - // Defined to decrease visibility - } - - @Override - public TemplateDateFormat get(String params, int dateType, Locale locale, TimeZone timeZone, boolean zonelessInput, - Environment env) throws UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException { - TemplateFormatUtil.checkHasNoParameters(params); - return AppMetaTemplateDateFormat.INSTANCE; - } - - private static class AppMetaTemplateDateFormat extends TemplateDateFormat { - - private static final AppMetaTemplateDateFormat INSTANCE = new AppMetaTemplateDateFormat(); - - private AppMetaTemplateDateFormat() { } - - @Override - public String formatToPlainText(TemplateDateModel dateModel) - throws UnformattableValueException, TemplateModelException { - String result = String.valueOf(TemplateFormatUtil.getNonNullDate(dateModel).getTime()); - if (dateModel instanceof AppMetaTemplateDateModel) { - result += "/" + ((AppMetaTemplateDateModel) dateModel).getAppMeta(); - } - return result; - } - - @Override - public boolean isLocaleBound() { - return false; - } - - @Override - public boolean isTimeZoneBound() { - return false; - } - - @Override - public Object parse(String s, int dateType) throws UnparsableValueException { - int slashIdx = s.indexOf('/'); - try { - if (slashIdx != -1) { - return new AppMetaTemplateDateModel( - new Date(Long.parseLong(s.substring(0, slashIdx))), - dateType, - s.substring(slashIdx +1)); - } else { - return new Date(Long.parseLong(s)); - } - } catch (NumberFormatException e) { - throw new UnparsableValueException("Malformed long"); - } - } - - @Override - public String getDescription() { - return "millis since the epoch"; - } - - } - - public static class AppMetaTemplateDateModel implements TemplateDateModel { - - private final Date date; - private final int dateType; - private final String appMeta; - - public AppMetaTemplateDateModel(Date date, int dateType, String appMeta) { - this.date = date; - this.dateType = dateType; - this.appMeta = appMeta; - } - - @Override - public Date getAsDate() throws TemplateModelException { - return date; - } - - @Override - public int getDateType() { - return dateType; - } - - public String getAppMeta() { - return appMeta; - } - - } - -}
