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&#39;b", out.toString());
+    }
+    
+    @Test
+    public void testOutputString() throws TemplateModelException, IOException {
+        StringWriter out = new StringWriter();
+        INSTANCE.output("a'b", out);
+        assertEquals("a&#39;b", out.toString());
+    }
+    
+    @Test
+    public void testEscaplePlainText() {
+        assertEquals("", INSTANCE.escapePlainText(""));
+        assertEquals("a", INSTANCE.escapePlainText("a"));
+        assertEquals("&lt;a&amp;b&#39;c&quot;d&gt;", 
INSTANCE.escapePlainText("<a&b'c\"d>"));
+        assertEquals("&lt;&gt;", 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&apos;b", out.toString());
+    }
+    
+    @Test
+    public void testOutputString() throws TemplateModelException, IOException {
+        StringWriter out = new StringWriter();
+        INSTANCE.output("a'b", out);
+        assertEquals("a&apos;b", out.toString());
+    }
+    
+    @Test
+    public void testEscaplePlainText() {
+        assertEquals("", INSTANCE.escapePlainText(""));
+        assertEquals("a", INSTANCE.escapePlainText("a"));
+        assertEquals("&lt;a&amp;b&apos;c&quot;d&gt;", 
INSTANCE.escapePlainText("<a&b'c\"d>"));
+        assertEquals("&lt;&gt;", 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;
-        }
-        
-    }
-
-}

Reply via email to