You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2015/12/19 17:43:00 UTC

[1/7] incubator-freemarker git commit: Removed unused olink definitions plus fixed one.

Repository: incubator-freemarker
Updated Branches:
  refs/heads/2.3-gae 0e5213a8b -> 41b6f8342


Removed unused olink definitions plus fixed one.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/159bcb29
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/159bcb29
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/159bcb29

Branch: refs/heads/2.3-gae
Commit: 159bcb29625d23cf74af7d5650d8db600ae7c94f
Parents: 0e5213a
Author: ddekany <dd...@apache.org>
Authored: Sat Dec 19 17:24:46 2015 +0100
Committer: ddekany <dd...@apache.org>
Committed: Sat Dec 19 17:24:46 2015 +0100

----------------------------------------------------------------------
 src/manual/docgen.cjson | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/159bcb29/src/manual/docgen.cjson
----------------------------------------------------------------------
diff --git a/src/manual/docgen.cjson b/src/manual/docgen.cjson
index 7bd3a78..221f349 100644
--- a/src/manual/docgen.cjson
+++ b/src/manual/docgen.cjson
@@ -42,15 +42,9 @@ olinks: {
   freemarkerdownload: "http://freemarker.org/freemarkerdownload.html"
   contribute: "http://freemarker.org/contribute.html"
   history: "http://freemarker.org/history.html"
-  what-is-freemarker: "http://freemarker.org/index.html"
+  what-is-freemarker: "id:preface"
   mailing-lists: "http://freemarker.org/mailing-lists.html"
   
-  // Manual links:
-  templateAuthorsGuide: "id:dgui_quickstart_basics"
-  license: "id:app_license"
-  versionHistory: "id:app_versions"
-  freemarkerServlet: "id:pgui_misc_servlet"
-  
   // External URL-s:
   onlineTemplateTester: "http://freemarker-online.kenshoo.com/"
   twitter: "https://twitter.com/freemarker"


[7/7] incubator-freemarker git commit: (Error message typo fix)

Posted by dd...@apache.org.
(Error message typo fix)


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/41b6f834
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/41b6f834
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/41b6f834

Branch: refs/heads/2.3-gae
Commit: 41b6f8342b3d6252d4927cd5729d2917934e500b
Parents: ea5c47d
Author: ddekany <dd...@apache.org>
Authored: Sat Dec 19 17:42:40 2015 +0100
Committer: ddekany <dd...@apache.org>
Committed: Sat Dec 19 17:42:40 2015 +0100

----------------------------------------------------------------------
 src/main/java/freemarker/core/Items.java | 2 +-
 src/main/java/freemarker/core/Sep.java   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/41b6f834/src/main/java/freemarker/core/Items.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Items.java b/src/main/java/freemarker/core/Items.java
index 38bc5a6..f588093 100644
--- a/src/main/java/freemarker/core/Items.java
+++ b/src/main/java/freemarker/core/Items.java
@@ -41,7 +41,7 @@ class Items extends TemplateElement {
         if (iterCtx == null) {
             // The parser should prevent this situation
             throw new _MiscTemplateException(env,
-                    getNodeTypeSymbol(), " without iteraton in context");
+                    getNodeTypeSymbol(), " without iteration in context");
         }
         
         iterCtx.loopForItemsElement(env, getChildBuffer(), loopVarName);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/41b6f834/src/main/java/freemarker/core/Sep.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Sep.java b/src/main/java/freemarker/core/Sep.java
index 0ae5de2..848bcd8 100644
--- a/src/main/java/freemarker/core/Sep.java
+++ b/src/main/java/freemarker/core/Sep.java
@@ -38,7 +38,7 @@ class Sep extends TemplateElement {
         if (iterCtx == null) {
             // The parser should prevent this situation
             throw new _MiscTemplateException(env,
-                    getNodeTypeSymbol(), " without iteraton in context");
+                    getNodeTypeSymbol(), " without iteration in context");
         }
         
         if (iterCtx.hasNext()) {


[4/7] incubator-freemarker git commit: The new (in 2.3.24-pre01) TemplateConfigurer class was renamed to TemplateConfiguration, and the related configuration setting from template_configurers to template_configurations. Also, the TemplateConfigurer.confi

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/cache/TemplateConfigurationFactoryTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/cache/TemplateConfigurationFactoryTest.java b/src/test/java/freemarker/cache/TemplateConfigurationFactoryTest.java
new file mode 100644
index 0000000..663eed2
--- /dev/null
+++ b/src/test/java/freemarker/cache/TemplateConfigurationFactoryTest.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.cache;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import freemarker.core.TemplateConfiguration;
+import freemarker.template.Configuration;
+
+public class TemplateConfigurationFactoryTest {
+    
+    private Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
+
+    @Test
+    public void testCondition1() throws IOException, TemplateConfigurationFactoryException {
+        TemplateConfiguration tc = newTemplateConfiguration(1);
+        
+        TemplateConfigurationFactory tcf = new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*.ftlx"), tc);
+        tcf.setConfiguration(cfg);
+        
+        assertNotApplicable(tcf, "x.ftl");
+        assertApplicable(tcf, "x.ftlx", tc);
+    }
+
+    @Test
+    public void testCondition2() throws IOException, TemplateConfigurationFactoryException {
+        TemplateConfiguration tc = newTemplateConfiguration(1);
+        
+        TemplateConfigurationFactory tcf = new ConditionalTemplateConfigurationFactory(
+                new FileNameGlobMatcher("*.ftlx"),
+                new ConditionalTemplateConfigurationFactory(
+                        new FileNameGlobMatcher("x.*"), tc));
+        tcf.setConfiguration(cfg);
+        
+        assertNotApplicable(tcf, "x.ftl");
+        assertNotApplicable(tcf, "y.ftlx");
+        assertApplicable(tcf, "x.ftlx", tc);
+    }
+
+    @Test
+    public void testMerging() throws IOException, TemplateConfigurationFactoryException {
+        TemplateConfiguration tc1 = newTemplateConfiguration(1);
+        TemplateConfiguration tc2 = newTemplateConfiguration(2);
+        TemplateConfiguration tc3 = newTemplateConfiguration(3);
+        
+        TemplateConfigurationFactory tcf = new MergingTemplateConfigurationFactory(
+                new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*.ftlx"), tc1),
+                new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*a*.*"), tc2),
+                new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*b*.*"), tc3));
+        tcf.setConfiguration(cfg);
+        
+        assertNotApplicable(tcf, "x.ftl");
+        assertApplicable(tcf, "x.ftlx", tc1);
+        assertApplicable(tcf, "a.ftl", tc2);
+        assertApplicable(tcf, "b.ftl", tc3);
+        assertApplicable(tcf, "a.ftlx", tc1, tc2);
+        assertApplicable(tcf, "b.ftlx", tc1, tc3);
+        assertApplicable(tcf, "ab.ftl", tc2, tc3);
+        assertApplicable(tcf, "ab.ftlx", tc1, tc2, tc3);
+        
+        assertNotApplicable(new MergingTemplateConfigurationFactory(), "x.ftl");
+    }
+
+    @Test
+    public void testFirstMatch() throws IOException, TemplateConfigurationFactoryException {
+        TemplateConfiguration tc1 = newTemplateConfiguration(1);
+        TemplateConfiguration tc2 = newTemplateConfiguration(2);
+        TemplateConfiguration tc3 = newTemplateConfiguration(3);
+        
+        FirstMatchTemplateConfigurationFactory tcf = new FirstMatchTemplateConfigurationFactory(
+                new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*.ftlx"), tc1),
+                new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*a*.*"), tc2),
+                new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*b*.*"), tc3));
+        tcf.setConfiguration(cfg);
+
+        try {
+            assertNotApplicable(tcf, "x.ftl");
+        } catch (TemplateConfigurationFactoryException e) {
+            assertThat(e.getMessage(), containsString("x.ftl"));
+        }
+        tcf.setNoMatchErrorDetails("Test details");
+        try {
+            assertNotApplicable(tcf, "x.ftl");
+        } catch (TemplateConfigurationFactoryException e) {
+            assertThat(e.getMessage(), containsString("Test details"));
+        }
+        
+        tcf.setAllowNoMatch(true);
+        
+        assertNotApplicable(tcf, "x.ftl");
+        assertApplicable(tcf, "x.ftlx", tc1);
+        assertApplicable(tcf, "a.ftl", tc2);
+        assertApplicable(tcf, "b.ftl", tc3);
+        assertApplicable(tcf, "a.ftlx", tc1);
+        assertApplicable(tcf, "b.ftlx", tc1);
+        assertApplicable(tcf, "ab.ftl", tc2);
+        assertApplicable(tcf, "ab.ftlx", tc1);
+        
+        assertNotApplicable(new FirstMatchTemplateConfigurationFactory().allowNoMatch(true), "x.ftl");
+    }
+
+    @Test
+    public void testComplex() throws IOException, TemplateConfigurationFactoryException {
+        TemplateConfiguration tcA = newTemplateConfiguration(1);
+        TemplateConfiguration tcBSpec = newTemplateConfiguration(2);
+        TemplateConfiguration tcBCommon = newTemplateConfiguration(3);
+        TemplateConfiguration tcHH = newTemplateConfiguration(4);
+        TemplateConfiguration tcHtml = newTemplateConfiguration(5);
+        TemplateConfiguration tcXml = newTemplateConfiguration(6);
+        TemplateConfiguration tcNWS = newTemplateConfiguration(7);
+        
+        TemplateConfigurationFactory tcf = new MergingTemplateConfigurationFactory(
+                new FirstMatchTemplateConfigurationFactory(
+                        new ConditionalTemplateConfigurationFactory(new PathGlobMatcher("a/**"), tcA),
+                        new ConditionalTemplateConfigurationFactory(new PathGlobMatcher("b/**"),
+                                new MergingTemplateConfigurationFactory(
+                                    new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*"), tcBCommon),
+                                    new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*.s.*"), tcBSpec))))
+                        .allowNoMatch(true),
+                new FirstMatchTemplateConfigurationFactory(
+                        new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*.hh"), tcHH),
+                        new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*.*h"), tcHtml),
+                        new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*.*x"), tcXml))
+                        .allowNoMatch(true),
+                new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*.nws.*"), tcNWS));
+        tcf.setConfiguration(cfg);
+        
+        assertNotApplicable(tcf, "x.ftl");
+        assertApplicable(tcf, "b/x.ftl", tcBCommon);
+        assertApplicable(tcf, "b/x.s.ftl", tcBCommon, tcBSpec);
+        assertApplicable(tcf, "b/x.s.ftlh", tcBCommon, tcBSpec, tcHtml);
+        assertApplicable(tcf, "b/x.s.nws.ftlx", tcBCommon, tcBSpec, tcXml, tcNWS);
+        assertApplicable(tcf, "a/x.s.nws.ftlx", tcA, tcXml, tcNWS);
+        assertApplicable(tcf, "a.hh", tcHH);
+        assertApplicable(tcf, "a.nws.hh", tcHH, tcNWS);
+    }
+
+    @Test
+    public void testSetConfiguration() {
+        TemplateConfiguration tc = new TemplateConfiguration();
+        ConditionalTemplateConfigurationFactory tcf = new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*"), tc);
+        assertNull(tcf.getConfiguration());
+        assertNull(tc.getParentConfiguration());
+        
+        tcf.setConfiguration(cfg);
+        assertEquals(cfg, tcf.getConfiguration());
+        assertEquals(cfg, tc.getParentConfiguration());
+        
+        // Ignored:
+        tcf.setConfiguration(cfg);
+        
+        try {
+            tcf.setConfiguration(Configuration.getDefaultConfiguration());
+            fail();
+        } catch (IllegalStateException e) {
+            assertThat(e.getMessage(), containsString("TemplateConfigurationFactory"));
+        }
+    }
+
+    @SuppressWarnings("boxing")
+    private TemplateConfiguration newTemplateConfiguration(int id) {
+        TemplateConfiguration tc = new TemplateConfiguration();
+        tc.setCustomAttribute("id", id);
+        tc.setCustomAttribute("contains" + id, true);
+        return tc;
+    }
+
+    private void assertNotApplicable(TemplateConfigurationFactory tcf, String sourceName)
+            throws IOException, TemplateConfigurationFactoryException {
+        assertNull(tcf.get(sourceName, "dummy"));
+    }
+
+    private void assertApplicable(TemplateConfigurationFactory tcf, String sourceName, TemplateConfiguration... expectedTCs)
+            throws IOException, TemplateConfigurationFactoryException {
+        TemplateConfiguration mergedTC = tcf.get(sourceName, "dummy");
+        assertNotNull("TC should have its parents Configuration set", mergedTC.getParentConfiguration());
+        List<String> mergedTCAttNames = Arrays.asList(mergedTC.getCustomAttributeNames());
+
+        for (TemplateConfiguration expectedTC : expectedTCs) {
+            Integer tcId = (Integer) expectedTC.getCustomAttribute("id");
+            if (tcId == null) {
+                fail("TemplateConfiguration-s must be created with newTemplateConfiguration(id) in this test");
+            }
+            if (!mergedTCAttNames.contains("contains" + tcId)) {
+                fail("TemplateConfiguration with ID " + tcId + " is missing from the asserted value");
+            }
+        }
+        
+        for (String attName: mergedTCAttNames) {
+            if (!containsCustomAttr(attName, expectedTCs)) {
+                fail("The asserted TemplateConfiguration contains an unexpected custom attribute: " + attName);
+            }
+        }
+        
+        assertEquals(expectedTCs[expectedTCs.length - 1].getCustomAttribute("id"), mergedTC.getCustomAttribute("id"));
+    }
+
+    private boolean containsCustomAttr(String attName, TemplateConfiguration... expectedTCs) {
+        for (TemplateConfiguration expectedTC : expectedTCs) {
+            if (expectedTC.getCustomAttribute(attName) != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/cache/TemplateConfigurerFactoryTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/cache/TemplateConfigurerFactoryTest.java b/src/test/java/freemarker/cache/TemplateConfigurerFactoryTest.java
deleted file mode 100644
index 500d088..0000000
--- a/src/test/java/freemarker/cache/TemplateConfigurerFactoryTest.java
+++ /dev/null
@@ -1,230 +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 freemarker.cache;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-
-import org.junit.Test;
-
-import freemarker.core.TemplateConfigurer;
-import freemarker.template.Configuration;
-
-public class TemplateConfigurerFactoryTest {
-    
-    private Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
-
-    @Test
-    public void testCondition1() throws IOException, TemplateConfigurerFactoryException {
-        TemplateConfigurer tc = newTemplateConfigurer(1);
-        
-        TemplateConfigurerFactory tcf = new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*.ftlx"), tc);
-        tcf.setConfiguration(cfg);
-        
-        assertNotApplicable(tcf, "x.ftl");
-        assertApplicable(tcf, "x.ftlx", tc);
-    }
-
-    @Test
-    public void testCondition2() throws IOException, TemplateConfigurerFactoryException {
-        TemplateConfigurer tc = newTemplateConfigurer(1);
-        
-        TemplateConfigurerFactory tcf = new ConditionalTemplateConfigurerFactory(
-                new FileNameGlobMatcher("*.ftlx"),
-                new ConditionalTemplateConfigurerFactory(
-                        new FileNameGlobMatcher("x.*"), tc));
-        tcf.setConfiguration(cfg);
-        
-        assertNotApplicable(tcf, "x.ftl");
-        assertNotApplicable(tcf, "y.ftlx");
-        assertApplicable(tcf, "x.ftlx", tc);
-    }
-
-    @Test
-    public void testMerging() throws IOException, TemplateConfigurerFactoryException {
-        TemplateConfigurer tc1 = newTemplateConfigurer(1);
-        TemplateConfigurer tc2 = newTemplateConfigurer(2);
-        TemplateConfigurer tc3 = newTemplateConfigurer(3);
-        
-        TemplateConfigurerFactory tcf = new MergingTemplateConfigurerFactory(
-                new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*.ftlx"), tc1),
-                new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*a*.*"), tc2),
-                new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*b*.*"), tc3));
-        tcf.setConfiguration(cfg);
-        
-        assertNotApplicable(tcf, "x.ftl");
-        assertApplicable(tcf, "x.ftlx", tc1);
-        assertApplicable(tcf, "a.ftl", tc2);
-        assertApplicable(tcf, "b.ftl", tc3);
-        assertApplicable(tcf, "a.ftlx", tc1, tc2);
-        assertApplicable(tcf, "b.ftlx", tc1, tc3);
-        assertApplicable(tcf, "ab.ftl", tc2, tc3);
-        assertApplicable(tcf, "ab.ftlx", tc1, tc2, tc3);
-        
-        assertNotApplicable(new MergingTemplateConfigurerFactory(), "x.ftl");
-    }
-
-    @Test
-    public void testFirstMatch() throws IOException, TemplateConfigurerFactoryException {
-        TemplateConfigurer tc1 = newTemplateConfigurer(1);
-        TemplateConfigurer tc2 = newTemplateConfigurer(2);
-        TemplateConfigurer tc3 = newTemplateConfigurer(3);
-        
-        FirstMatchTemplateConfigurerFactory tcf = new FirstMatchTemplateConfigurerFactory(
-                new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*.ftlx"), tc1),
-                new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*a*.*"), tc2),
-                new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*b*.*"), tc3));
-        tcf.setConfiguration(cfg);
-
-        try {
-            assertNotApplicable(tcf, "x.ftl");
-        } catch (TemplateConfigurerFactoryException e) {
-            assertThat(e.getMessage(), containsString("x.ftl"));
-        }
-        tcf.setNoMatchErrorDetails("Test details");
-        try {
-            assertNotApplicable(tcf, "x.ftl");
-        } catch (TemplateConfigurerFactoryException e) {
-            assertThat(e.getMessage(), containsString("Test details"));
-        }
-        
-        tcf.setAllowNoMatch(true);
-        
-        assertNotApplicable(tcf, "x.ftl");
-        assertApplicable(tcf, "x.ftlx", tc1);
-        assertApplicable(tcf, "a.ftl", tc2);
-        assertApplicable(tcf, "b.ftl", tc3);
-        assertApplicable(tcf, "a.ftlx", tc1);
-        assertApplicable(tcf, "b.ftlx", tc1);
-        assertApplicable(tcf, "ab.ftl", tc2);
-        assertApplicable(tcf, "ab.ftlx", tc1);
-        
-        assertNotApplicable(new FirstMatchTemplateConfigurerFactory().allowNoMatch(true), "x.ftl");
-    }
-
-    @Test
-    public void testComplex() throws IOException, TemplateConfigurerFactoryException {
-        TemplateConfigurer tcA = newTemplateConfigurer(1);
-        TemplateConfigurer tcBSpec = newTemplateConfigurer(2);
-        TemplateConfigurer tcBCommon = newTemplateConfigurer(3);
-        TemplateConfigurer tcHH = newTemplateConfigurer(4);
-        TemplateConfigurer tcHtml = newTemplateConfigurer(5);
-        TemplateConfigurer tcXml = newTemplateConfigurer(6);
-        TemplateConfigurer tcNWS = newTemplateConfigurer(7);
-        
-        TemplateConfigurerFactory tcf = new MergingTemplateConfigurerFactory(
-                new FirstMatchTemplateConfigurerFactory(
-                        new ConditionalTemplateConfigurerFactory(new PathGlobMatcher("a/**"), tcA),
-                        new ConditionalTemplateConfigurerFactory(new PathGlobMatcher("b/**"),
-                                new MergingTemplateConfigurerFactory(
-                                    new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*"), tcBCommon),
-                                    new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*.s.*"), tcBSpec))))
-                        .allowNoMatch(true),
-                new FirstMatchTemplateConfigurerFactory(
-                        new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*.hh"), tcHH),
-                        new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*.*h"), tcHtml),
-                        new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*.*x"), tcXml))
-                        .allowNoMatch(true),
-                new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*.nws.*"), tcNWS));
-        tcf.setConfiguration(cfg);
-        
-        assertNotApplicable(tcf, "x.ftl");
-        assertApplicable(tcf, "b/x.ftl", tcBCommon);
-        assertApplicable(tcf, "b/x.s.ftl", tcBCommon, tcBSpec);
-        assertApplicable(tcf, "b/x.s.ftlh", tcBCommon, tcBSpec, tcHtml);
-        assertApplicable(tcf, "b/x.s.nws.ftlx", tcBCommon, tcBSpec, tcXml, tcNWS);
-        assertApplicable(tcf, "a/x.s.nws.ftlx", tcA, tcXml, tcNWS);
-        assertApplicable(tcf, "a.hh", tcHH);
-        assertApplicable(tcf, "a.nws.hh", tcHH, tcNWS);
-    }
-
-    @Test
-    public void testSetConfiguration() {
-        TemplateConfigurer tc = new TemplateConfigurer();
-        ConditionalTemplateConfigurerFactory tcf = new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*"), tc);
-        assertNull(tcf.getConfiguration());
-        assertNull(tc.getParentConfiguration());
-        
-        tcf.setConfiguration(cfg);
-        assertEquals(cfg, tcf.getConfiguration());
-        assertEquals(cfg, tc.getParentConfiguration());
-        
-        // Ignored:
-        tcf.setConfiguration(cfg);
-        
-        try {
-            tcf.setConfiguration(Configuration.getDefaultConfiguration());
-            fail();
-        } catch (IllegalStateException e) {
-            assertThat(e.getMessage(), containsString("TemplateConfigurerFactory"));
-        }
-    }
-
-    @SuppressWarnings("boxing")
-    private TemplateConfigurer newTemplateConfigurer(int id) {
-        TemplateConfigurer tc = new TemplateConfigurer();
-        tc.setCustomAttribute("id", id);
-        tc.setCustomAttribute("contains" + id, true);
-        return tc;
-    }
-
-    private void assertNotApplicable(TemplateConfigurerFactory tcf, String sourceName)
-            throws IOException, TemplateConfigurerFactoryException {
-        assertNull(tcf.get(sourceName, "dummy"));
-    }
-
-    private void assertApplicable(TemplateConfigurerFactory tcf, String sourceName, TemplateConfigurer... expectedTCs)
-            throws IOException, TemplateConfigurerFactoryException {
-        TemplateConfigurer mergedTC = tcf.get(sourceName, "dummy");
-        assertNotNull("TC should have its parents Configuration set", mergedTC.getParentConfiguration());
-        List<String> mergedTCAttNames = Arrays.asList(mergedTC.getCustomAttributeNames());
-
-        for (TemplateConfigurer expectedTC : expectedTCs) {
-            Integer tcId = (Integer) expectedTC.getCustomAttribute("id");
-            if (tcId == null) {
-                fail("TemplateConfigurer-s must be created with newTemplateConfigurer(id) in this test");
-            }
-            if (!mergedTCAttNames.contains("contains" + tcId)) {
-                fail("TemplateConfigurer with ID " + tcId + " is missing from the asserted value");
-            }
-        }
-        
-        for (String attName: mergedTCAttNames) {
-            if (!containsCustomAttr(attName, expectedTCs)) {
-                fail("The asserted TemplateConfigurer contains an unexpected custom attribute: " + attName);
-            }
-        }
-        
-        assertEquals(expectedTCs[expectedTCs.length - 1].getCustomAttribute("id"), mergedTC.getCustomAttribute("id"));
-    }
-
-    private boolean containsCustomAttr(String attName, TemplateConfigurer... expectedTCs) {
-        for (TemplateConfigurer expectedTC : expectedTCs) {
-            if (expectedTC.getCustomAttribute(attName) != null) {
-                return true;
-            }
-        }
-        return false;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/core/DateFormatTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/DateFormatTest.java b/src/test/java/freemarker/core/DateFormatTest.java
index 20b2cc9..792b9b2 100644
--- a/src/test/java/freemarker/core/DateFormatTest.java
+++ b/src/test/java/freemarker/core/DateFormatTest.java
@@ -34,7 +34,7 @@ import org.junit.Test;
 
 import com.google.common.collect.ImmutableMap;
 
-import freemarker.cache.ConditionalTemplateConfigurerFactory;
+import freemarker.cache.ConditionalTemplateConfigurationFactory;
 import freemarker.cache.FileNameGlobMatcher;
 import freemarker.template.Configuration;
 import freemarker.template.SimpleDate;
@@ -347,11 +347,11 @@ public class DateFormatTest extends TemplateTest {
                 "m", new AliasTemplateDateFormatFactory("yyyy-MMM"),
                 "epoch", EpochMillisTemplateDateFormatFactory.INSTANCE));
         
-        TemplateConfigurer tc = new TemplateConfigurer();
+        TemplateConfiguration tc = new TemplateConfiguration();
         tc.setCustomDateFormats(ImmutableMap.of(
                 "m", new AliasTemplateDateFormatFactory("yyyy-MMMM"),
                 "i", new AliasTemplateDateFormatFactory("@epoch")));
-        cfg.setTemplateConfigurers(new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*2*"), tc));
+        cfg.setTemplateConfigurations(new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*2*"), tc));
         
         addToDataModel("d", TM);
         String commonFtl = "${d?string.@d} ${d?string.@m} "

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/core/NumberFormatTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/NumberFormatTest.java b/src/test/java/freemarker/core/NumberFormatTest.java
index 19d2cba..dee402f 100644
--- a/src/test/java/freemarker/core/NumberFormatTest.java
+++ b/src/test/java/freemarker/core/NumberFormatTest.java
@@ -33,7 +33,7 @@ import org.junit.Test;
 
 import com.google.common.collect.ImmutableMap;
 
-import freemarker.cache.ConditionalTemplateConfigurerFactory;
+import freemarker.cache.ConditionalTemplateConfigurationFactory;
 import freemarker.cache.FileNameGlobMatcher;
 import freemarker.template.Configuration;
 import freemarker.template.SimpleNumber;
@@ -251,11 +251,11 @@ public class NumberFormatTest extends TemplateTest {
                 "d", new AliasTemplateNumberFormatFactory("0.0#"),
                 "hex", HexTemplateNumberFormatFactory.INSTANCE));
         
-        TemplateConfigurer tc = new TemplateConfigurer();
+        TemplateConfiguration tc = new TemplateConfiguration();
         tc.setCustomNumberFormats(ImmutableMap.of(
                 "d", new AliasTemplateNumberFormatFactory("0.#'d'"),
                 "i", new AliasTemplateNumberFormatFactory("@hex")));
-        cfg.setTemplateConfigurers(new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*2*"), tc));
+        cfg.setTemplateConfigurations(new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*2*"), tc));
         
         String commonFtl = "${1?string.@f} ${1?string.@d} "
                 + "<#setting locale='fr_FR'>${1.5?string.@d} "

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/core/OutputFormatTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/OutputFormatTest.java b/src/test/java/freemarker/core/OutputFormatTest.java
index b596437..cc31920 100644
--- a/src/test/java/freemarker/core/OutputFormatTest.java
+++ b/src/test/java/freemarker/core/OutputFormatTest.java
@@ -29,7 +29,7 @@ import org.junit.Test;
 
 import com.google.common.collect.ImmutableList;
 
-import freemarker.cache.ConditionalTemplateConfigurerFactory;
+import freemarker.cache.ConditionalTemplateConfigurationFactory;
 import freemarker.cache.FileNameGlobMatcher;
 import freemarker.cache.OrMatcher;
 import freemarker.template.Configuration;
@@ -111,10 +111,10 @@ public class OutputFormatTest extends TemplateTest {
             case 3:
                 cfgOutputFormat = UndefinedOutputFormat.INSTANCE;
                 cfg.unsetOutputFormat();
-                TemplateConfigurer tcXml = new TemplateConfigurer();
+                TemplateConfiguration tcXml = new TemplateConfiguration();
                 tcXml.setOutputFormat(XMLOutputFormat.INSTANCE);
-                cfg.setTemplateConfigurers(
-                        new ConditionalTemplateConfigurerFactory(
+                cfg.setTemplateConfigurations(
+                        new ConditionalTemplateConfigurationFactory(
                                 new OrMatcher(
                                         new FileNameGlobMatcher("*.ftlh"),
                                         new FileNameGlobMatcher("*.FTLH"),
@@ -130,7 +130,7 @@ public class OutputFormatTest extends TemplateTest {
                 ftlxOutputFormat = UndefinedOutputFormat.INSTANCE;
                 break;
             case 5:
-                cfg.setTemplateConfigurers(null);
+                cfg.setTemplateConfigurations(null);
                 cfgOutputFormat = UndefinedOutputFormat.INSTANCE;
                 ftlhOutputFormat = UndefinedOutputFormat.INSTANCE;
                 ftlxOutputFormat = UndefinedOutputFormat.INSTANCE;
@@ -182,25 +182,25 @@ public class OutputFormatTest extends TemplateTest {
         addTemplate("t.ftl",
                 "${'{}'} ${'{}'?esc} ${'{}'?noEsc}");
         
-        TemplateConfigurer tcHTML = new TemplateConfigurer();
+        TemplateConfiguration tcHTML = new TemplateConfiguration();
         tcHTML.setOutputFormat(HTMLOutputFormat.INSTANCE);
-        ConditionalTemplateConfigurerFactory tcfHTML = new ConditionalTemplateConfigurerFactory(
+        ConditionalTemplateConfigurationFactory tcfHTML = new ConditionalTemplateConfigurationFactory(
                 new FileNameGlobMatcher("t.*"), tcHTML);
 
-        TemplateConfigurer tcNoAutoEsc = new TemplateConfigurer();
+        TemplateConfiguration tcNoAutoEsc = new TemplateConfiguration();
         tcNoAutoEsc.setAutoEscapingPolicy(Configuration.DISABLE_AUTO_ESCAPING_POLICY);
-        ConditionalTemplateConfigurerFactory tcfNoAutoEsc = new ConditionalTemplateConfigurerFactory(
+        ConditionalTemplateConfigurationFactory tcfNoAutoEsc = new ConditionalTemplateConfigurationFactory(
                 new FileNameGlobMatcher("t.*"), tcNoAutoEsc);
 
         Configuration cfg = getConfiguration();
         cfg.setOutputFormat(HTMLOutputFormat.INSTANCE);
         assertOutputForNamed("t.ftlx", "&apos; &apos; '");  // Can't override it
-        cfg.setTemplateConfigurers(tcfHTML);
+        cfg.setTemplateConfigurations(tcfHTML);
         assertOutputForNamed("t.ftlx", "&apos; &apos; '");  // Can't override it
-        cfg.setTemplateConfigurers(tcfNoAutoEsc);
+        cfg.setTemplateConfigurations(tcfNoAutoEsc);
         assertOutputForNamed("t.ftlx", "&apos; &apos; '");  // Can't override it
         
-        cfg.setTemplateConfigurers(null);
+        cfg.setTemplateConfigurations(null);
         cfg.unsetOutputFormat();
         cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_23);  // Extensions has no effect
         assertErrorContainsForNamed("t.ftlx", UndefinedOutputFormat.INSTANCE.getName());
@@ -208,21 +208,21 @@ public class OutputFormatTest extends TemplateTest {
         assertOutputForNamed("t.ftlx", "&#39; &#39; '");
         cfg.setOutputFormat(XMLOutputFormat.INSTANCE);
         assertOutputForNamed("t.ftlx", "&apos; &apos; '");
-        cfg.setTemplateConfigurers(tcfHTML);
+        cfg.setTemplateConfigurations(tcfHTML);
         assertOutputForNamed("t.ftlx", "&#39; &#39; '");
-        cfg.setTemplateConfigurers(tcfNoAutoEsc);
+        cfg.setTemplateConfigurations(tcfNoAutoEsc);
         assertOutputForNamed("t.ftlx", "' &apos; '");
         
         cfg.setRecognizeStandardFileExtensions(true);
-        cfg.setTemplateConfigurers(tcfHTML);
+        cfg.setTemplateConfigurations(tcfHTML);
         assertOutputForNamed("t.ftlx", "&apos; &apos; '");  // Can't override it
-        cfg.setTemplateConfigurers(tcfNoAutoEsc);
+        cfg.setTemplateConfigurations(tcfNoAutoEsc);
         assertOutputForNamed("t.ftlx", "&apos; &apos; '");  // Can't override it
         
-        cfg.setTemplateConfigurers(null);
+        cfg.setTemplateConfigurations(null);
         cfg.unsetOutputFormat();
         cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_24);
-        cfg.setTemplateConfigurers(tcfHTML);
+        cfg.setTemplateConfigurations(tcfHTML);
         assertOutputForNamed("t.ftlx", "&apos; &apos; '");  // Can't override it
         cfg.setRecognizeStandardFileExtensions(false);
         assertOutputForNamed("t.ftlx", "&#39; &#39; '");
@@ -1005,10 +1005,10 @@ public class OutputFormatTest extends TemplateTest {
     protected Configuration createConfiguration() throws TemplateModelException {
         Configuration cfg = new Configuration(Configuration.VERSION_2_3_24);
         
-        TemplateConfigurer xmlTC = new TemplateConfigurer();
+        TemplateConfiguration xmlTC = new TemplateConfiguration();
         xmlTC.setOutputFormat(XMLOutputFormat.INSTANCE);
-        cfg.setTemplateConfigurers(
-                new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*.xml"), xmlTC));
+        cfg.setTemplateConfigurations(
+                new ConditionalTemplateConfigurationFactory(new FileNameGlobMatcher("*.xml"), xmlTC));
 
         cfg.setSharedVariable("rtfPlain", RTFOutputFormat.INSTANCE.fromPlainTextByEscaping("\\par a & b"));
         cfg.setSharedVariable("rtfMarkup", RTFOutputFormat.INSTANCE.fromMarkup("\\par c"));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/core/TemplateConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/TemplateConfigurationTest.java b/src/test/java/freemarker/core/TemplateConfigurationTest.java
new file mode 100644
index 0000000..99b1b7a
--- /dev/null
+++ b/src/test/java/freemarker/core/TemplateConfigurationTest.java
@@ -0,0 +1,846 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.core;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+import freemarker.template.Configuration;
+import freemarker.template.SimpleObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateExceptionHandler;
+import freemarker.template.Version;
+import freemarker.template.utility.NullArgumentException;
+
+@SuppressWarnings("boxing")
+public class TemplateConfigurationTest {
+
+    private final class DummyArithmeticEngine extends ArithmeticEngine {
+
+        @Override
+        public int compareNumbers(Number first, Number second) throws TemplateException {
+            return 0;
+        }
+
+        @Override
+        public Number add(Number first, Number second) throws TemplateException {
+            return 22;
+        }
+
+        @Override
+        public Number subtract(Number first, Number second) throws TemplateException {
+            return null;
+        }
+
+        @Override
+        public Number multiply(Number first, Number second) throws TemplateException {
+            return 33;
+        }
+
+        @Override
+        public Number divide(Number first, Number second) throws TemplateException {
+            return null;
+        }
+
+        @Override
+        public Number modulus(Number first, Number second) throws TemplateException {
+            return null;
+        }
+
+        @Override
+        public Number toNumber(String s) {
+            return 11;
+        }
+    }
+
+    private static final Version ICI = Configuration.VERSION_2_3_22;
+
+    private static final Configuration DEFAULT_CFG = new Configuration(ICI);
+
+    private static final TimeZone NON_DEFAULT_TZ;
+    static {
+        TimeZone defaultTZ = DEFAULT_CFG.getTimeZone();
+        TimeZone tz = TimeZone.getTimeZone("UTC");
+        if (tz.equals(defaultTZ)) {
+            tz = TimeZone.getTimeZone("GMT+01");
+            if (tz.equals(defaultTZ)) {
+                throw new AssertionError("Couldn't chose a non-default time zone");
+            }
+        }
+        NON_DEFAULT_TZ = tz;
+    }
+
+    private static final Locale NON_DEFAULT_LOCALE;
+    static {
+        Locale defaultLocale = DEFAULT_CFG.getLocale();
+        Locale locale = Locale.GERMAN;
+        if (locale.equals(defaultLocale)) {
+            locale = Locale.US;
+            if (locale.equals(defaultLocale)) {
+                throw new AssertionError("Couldn't chose a non-default locale");
+            }
+        }
+        NON_DEFAULT_LOCALE = locale;
+    }
+
+    private static final String NON_DEFAULT_ENCODING;
+
+    static {
+        String defaultEncoding = DEFAULT_CFG.getDefaultEncoding();
+        String encoding = "UTF-16";
+        if (encoding.equals(defaultEncoding)) {
+            encoding = "UTF-8";
+            if (encoding.equals(defaultEncoding)) {
+                throw new AssertionError("Couldn't chose a non-default locale");
+            }
+        }
+        NON_DEFAULT_ENCODING = encoding;
+    }
+    
+    private static final Map<String, Object> SETTING_ASSIGNMENTS;
+
+    static {
+        SETTING_ASSIGNMENTS = new HashMap<String, Object>();
+
+        // "Configurable" settings:
+        SETTING_ASSIGNMENTS.put("APIBuiltinEnabled", true);
+        SETTING_ASSIGNMENTS.put("SQLDateAndTimeTimeZone", NON_DEFAULT_TZ);
+        SETTING_ASSIGNMENTS.put("URLEscapingCharset", "utf-16");
+        SETTING_ASSIGNMENTS.put("autoFlush", false);
+        SETTING_ASSIGNMENTS.put("booleanFormat", "J,N");
+        SETTING_ASSIGNMENTS.put("classicCompatibleAsInt", 2);
+        SETTING_ASSIGNMENTS.put("dateFormat", "yyyy-#DDD");
+        SETTING_ASSIGNMENTS.put("dateTimeFormat", "yyyy-#DDD-@HH:mm");
+        SETTING_ASSIGNMENTS.put("locale", NON_DEFAULT_LOCALE);
+        SETTING_ASSIGNMENTS.put("logTemplateExceptions", false);
+        SETTING_ASSIGNMENTS.put("newBuiltinClassResolver", TemplateClassResolver.ALLOWS_NOTHING_RESOLVER);
+        SETTING_ASSIGNMENTS.put("numberFormat", "0.0000");
+        SETTING_ASSIGNMENTS.put("objectWrapper", new SimpleObjectWrapper(ICI));
+        SETTING_ASSIGNMENTS.put("outputEncoding", "utf-16");
+        SETTING_ASSIGNMENTS.put("showErrorTips", false);
+        SETTING_ASSIGNMENTS.put("templateExceptionHandler", TemplateExceptionHandler.IGNORE_HANDLER);
+        SETTING_ASSIGNMENTS.put("timeFormat", "@HH:mm");
+        SETTING_ASSIGNMENTS.put("timeZone", NON_DEFAULT_TZ);
+        SETTING_ASSIGNMENTS.put("arithmeticEngine", ArithmeticEngine.CONSERVATIVE_ENGINE);
+        SETTING_ASSIGNMENTS.put("customNumberFormats",
+                ImmutableMap.of("dummy", HexTemplateNumberFormatFactory.INSTANCE));
+        SETTING_ASSIGNMENTS.put("customDateFormats",
+                ImmutableMap.of("dummy", EpochMillisTemplateDateFormatFactory.INSTANCE));
+
+        // Parser-only settings:
+        SETTING_ASSIGNMENTS.put("tagSyntax", Configuration.SQUARE_BRACKET_TAG_SYNTAX);
+        SETTING_ASSIGNMENTS.put("namingConvention", Configuration.LEGACY_NAMING_CONVENTION);
+        SETTING_ASSIGNMENTS.put("whitespaceStripping", false);
+        SETTING_ASSIGNMENTS.put("strictSyntaxMode", false);
+        SETTING_ASSIGNMENTS.put("autoEscapingPolicy", Configuration.DISABLE_AUTO_ESCAPING_POLICY);
+        SETTING_ASSIGNMENTS.put("outputFormat", HTMLOutputFormat.INSTANCE);
+        SETTING_ASSIGNMENTS.put("recognizeStandardFileExtensions", true);
+        
+        // Special settings:
+        SETTING_ASSIGNMENTS.put("encoding", NON_DEFAULT_ENCODING);
+    }
+    
+    public static String getIsSetMethodName(String readMethodName) {
+        String isSetMethodName = (readMethodName.startsWith("get") ? "is" + readMethodName.substring(3)
+                : readMethodName)
+                + "Set";
+        if (isSetMethodName.equals("isClassicCompatibleAsIntSet")) {
+            isSetMethodName = "isClassicCompatibleSet";
+        }
+        return isSetMethodName;
+    }
+
+    public static List<PropertyDescriptor> getTemplateConfigurationSettingPropDescs(
+            boolean includeCompilerSettings, boolean includeSpecialSettings)
+            throws IntrospectionException {
+        List<PropertyDescriptor> settingPropDescs = new ArrayList<PropertyDescriptor>();
+
+        BeanInfo beanInfo = Introspector.getBeanInfo(TemplateConfiguration.class);
+        for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
+            String name = pd.getName();
+            if (pd.getWriteMethod() != null && !IGNORED_PROP_NAMES.contains(name)
+                    && (includeCompilerSettings
+                            || (CONFIGURABLE_PROP_NAMES.contains(name) || !PARSER_PROP_NAMES.contains(name)))
+                    && (includeSpecialSettings
+                            || !SPECIAL_PROP_NAMES.contains(name))) {
+                if (pd.getReadMethod() == null) {
+                    throw new AssertionError("Property has no read method: " + pd);
+                }
+                settingPropDescs.add(pd);
+            }
+        }
+
+        Collections.sort(settingPropDescs, new Comparator<PropertyDescriptor>() {
+
+            public int compare(PropertyDescriptor o1, PropertyDescriptor o2) {
+                return o1.getName().compareToIgnoreCase(o2.getName());
+            }
+        });
+
+        return settingPropDescs;
+    }
+
+    private static final Set<String> IGNORED_PROP_NAMES;
+
+    static {
+        IGNORED_PROP_NAMES = new HashSet();
+        IGNORED_PROP_NAMES.add("class");
+        IGNORED_PROP_NAMES.add("strictBeanModels");
+        IGNORED_PROP_NAMES.add("parentConfiguration");
+        IGNORED_PROP_NAMES.add("settings");
+        IGNORED_PROP_NAMES.add("classicCompatible");
+    }
+
+    private static final Set<String> CONFIGURABLE_PROP_NAMES;
+    static {
+        CONFIGURABLE_PROP_NAMES = new HashSet<String>();
+        try {
+            for (PropertyDescriptor propDesc : Introspector.getBeanInfo(Configurable.class).getPropertyDescriptors()) {
+                String propName = propDesc.getName();
+                if (!IGNORED_PROP_NAMES.contains(propName)) {
+                    CONFIGURABLE_PROP_NAMES.add(propName);
+                }
+            }
+        } catch (IntrospectionException e) {
+            throw new IllegalStateException("Failed to init static field", e);
+        }
+    }
+    
+    private static final Set<String> PARSER_PROP_NAMES;
+    static {
+        PARSER_PROP_NAMES = new HashSet<String>();
+        // It's an interface; can't use standard Inrospector
+        for (Method m : ParserConfiguration.class.getMethods()) {
+            String propertyName;
+            if (m.getName().startsWith("get")) {
+                propertyName = m.getName().substring(3);
+            } else if (m.getName().startsWith("is")) {
+                propertyName = m.getName().substring(2);
+            } else {
+                propertyName = null;
+            }
+            if (propertyName != null) {
+                if (!Character.isUpperCase(propertyName.charAt(1))) {
+                    propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
+                }
+                PARSER_PROP_NAMES.add(propertyName);
+            }
+        }
+    }
+
+    private static final Set<String> SPECIAL_PROP_NAMES;
+    static {
+        SPECIAL_PROP_NAMES = new HashSet<String>();
+        SPECIAL_PROP_NAMES.add("encoding");
+    }
+    
+    private static final CustomAttribute CA1 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); 
+    private static final CustomAttribute CA2 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); 
+    private static final CustomAttribute CA3 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); 
+    private static final CustomAttribute CA4 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); 
+
+    @Test
+    public void testMergeBasicFunctionality() throws Exception {
+        for (PropertyDescriptor propDesc1 : getTemplateConfigurationSettingPropDescs(true, true)) {
+            for (PropertyDescriptor propDesc2 : getTemplateConfigurationSettingPropDescs(true, true)) {
+                TemplateConfiguration tc1 = new TemplateConfiguration();
+                TemplateConfiguration tc2 = new TemplateConfiguration();
+
+                Object value1 = SETTING_ASSIGNMENTS.get(propDesc1.getName());
+                propDesc1.getWriteMethod().invoke(tc1, value1);
+                Object value2 = SETTING_ASSIGNMENTS.get(propDesc2.getName());
+                propDesc2.getWriteMethod().invoke(tc2, value2);
+
+                tc1.merge(tc2);
+                Object mValue1 = propDesc1.getReadMethod().invoke(tc1);
+                Object mValue2 = propDesc2.getReadMethod().invoke(tc1);
+
+                assertEquals("For " + propDesc1.getName(), value1, mValue1);
+                assertEquals("For " + propDesc2.getName(), value2, mValue2);
+            }
+        }
+    }
+    
+    @Test
+    public void testMergeMapSettings() throws Exception {
+        TemplateConfiguration tc1 = new TemplateConfiguration();
+        tc1.setCustomDateFormats(ImmutableMap.of(
+                "epoch", EpochMillisTemplateDateFormatFactory.INSTANCE,
+                "x", LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE));
+        tc1.setCustomNumberFormats(ImmutableMap.of(
+                "hex", HexTemplateNumberFormatFactory.INSTANCE,
+                "x", LocaleSensitiveTemplateNumberFormatFactory.INSTANCE));
+        
+        TemplateConfiguration tc2 = new TemplateConfiguration();
+        tc2.setCustomDateFormats(ImmutableMap.of(
+                "loc", LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE,
+                "x", EpochMillisDivTemplateDateFormatFactory.INSTANCE));
+        tc2.setCustomNumberFormats(ImmutableMap.of(
+                "loc", LocaleSensitiveTemplateNumberFormatFactory.INSTANCE,
+                "x", BaseNTemplateNumberFormatFactory.INSTANCE));
+        
+        tc1.merge(tc2);
+        
+        Map<String, ? extends TemplateDateFormatFactory> mergedCustomDateFormats = tc1.getCustomDateFormats();
+        assertEquals(EpochMillisTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("epoch"));
+        assertEquals(LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("loc"));
+        assertEquals(EpochMillisDivTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("x"));
+        
+        Map<String, ? extends TemplateNumberFormatFactory> mergedCustomNumberFormats = tc1.getCustomNumberFormats();
+        assertEquals(HexTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("hex"));
+        assertEquals(LocaleSensitiveTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("loc"));
+        assertEquals(BaseNTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("x"));
+        
+        // Empty map merging optimization:
+        tc1.merge(new TemplateConfiguration());
+        assertSame(mergedCustomDateFormats, tc1.getCustomDateFormats());
+        assertSame(mergedCustomNumberFormats, tc1.getCustomNumberFormats());
+        
+        // Empty map merging optimization:
+        TemplateConfiguration tc3 = new TemplateConfiguration();
+        tc3.merge(tc1);
+        assertSame(mergedCustomDateFormats, tc3.getCustomDateFormats());
+        assertSame(mergedCustomNumberFormats, tc3.getCustomNumberFormats());
+    }
+    
+    @Test
+    public void testMergePriority() throws Exception {
+        TemplateConfiguration tc1 = new TemplateConfiguration();
+        tc1.setDateFormat("1");
+        tc1.setTimeFormat("1");
+        tc1.setDateTimeFormat("1");
+
+        TemplateConfiguration tc2 = new TemplateConfiguration();
+        tc2.setDateFormat("2");
+        tc2.setTimeFormat("2");
+
+        TemplateConfiguration tc3 = new TemplateConfiguration();
+        tc3.setDateFormat("3");
+
+        tc1.merge(tc2);
+        tc1.merge(tc3);
+
+        assertEquals("3", tc1.getDateFormat());
+        assertEquals("2", tc1.getTimeFormat());
+        assertEquals("1", tc1.getDateTimeFormat());
+    }
+    
+    @Test
+    public void testMergeCustomAttributes() throws Exception {
+        TemplateConfiguration tc1 = new TemplateConfiguration();
+        tc1.setCustomAttribute("k1", "v1");
+        tc1.setCustomAttribute("k2", "v1");
+        tc1.setCustomAttribute("k3", "v1");
+        CA1.set("V1", tc1);
+        CA2.set("V1", tc1);
+        CA3.set("V1", tc1);
+
+        TemplateConfiguration tc2 = new TemplateConfiguration();
+        tc2.setCustomAttribute("k1", "v2");
+        tc2.setCustomAttribute("k2", "v2");
+        CA1.set("V2", tc2);
+        CA2.set("V2", tc2);
+
+        TemplateConfiguration tc3 = new TemplateConfiguration();
+        tc3.setCustomAttribute("k1", "v3");
+        CA1.set("V3", tc2);
+
+        tc1.merge(tc2);
+        tc1.merge(tc3);
+
+        assertEquals("v3", tc1.getCustomAttribute("k1"));
+        assertEquals("v2", tc1.getCustomAttribute("k2"));
+        assertEquals("v1", tc1.getCustomAttribute("k3"));
+        assertEquals("V3", CA1.get(tc1));
+        assertEquals("V2", CA2.get(tc1));
+        assertEquals("V1", CA3.get(tc1));
+    }
+    
+    @Test
+    public void testMergeNullCustomAttributes() throws Exception {
+        TemplateConfiguration tc1 = new TemplateConfiguration();
+        tc1.setCustomAttribute("k1", "v1");
+        tc1.setCustomAttribute("k2", "v1");
+        tc1.setCustomAttribute(null, "v1");
+        CA1.set("V1", tc1);
+        CA2.set("V1", tc1);
+        CA3.set(null, tc1);
+        
+        assertEquals("v1", tc1.getCustomAttribute("k1"));
+        assertEquals("v1", tc1.getCustomAttribute("k2"));
+        assertNull("v1", tc1.getCustomAttribute("k3"));
+        assertEquals("V1", CA1.get(tc1));
+        assertEquals("V1", CA2.get(tc1));
+        assertNull(CA3.get(tc1));
+
+        TemplateConfiguration tc2 = new TemplateConfiguration();
+        tc2.setCustomAttribute("k1", "v2");
+        tc2.setCustomAttribute("k2", null);
+        CA1.set("V2", tc2);
+        CA2.set(null, tc2);
+
+        TemplateConfiguration tc3 = new TemplateConfiguration();
+        tc3.setCustomAttribute("k1", null);
+        CA1.set(null, tc2);
+
+        tc1.merge(tc2);
+        tc1.merge(tc3);
+
+        assertNull(tc1.getCustomAttribute("k1"));
+        assertNull(tc1.getCustomAttribute("k2"));
+        assertNull(tc1.getCustomAttribute("k3"));
+        assertNull(CA1.get(tc1));
+        assertNull(CA2.get(tc1));
+        assertNull(CA3.get(tc1));
+        
+        TemplateConfiguration tc4 = new TemplateConfiguration();
+        tc4.setCustomAttribute("k1", "v4");
+        CA1.set("V4", tc4);
+        
+        tc1.merge(tc4);
+        
+        assertEquals("v4", tc1.getCustomAttribute("k1"));
+        assertNull(tc1.getCustomAttribute("k2"));
+        assertNull(tc1.getCustomAttribute("k3"));
+        assertEquals("V4", CA1.get(tc1));
+        assertNull(CA2.get(tc1));
+        assertNull(CA3.get(tc1));
+    }
+
+    @Test
+    public void testConfigureNonParserConfig() throws Exception {
+        for (PropertyDescriptor pd : getTemplateConfigurationSettingPropDescs(false, true)) {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+    
+            Object newValue = SETTING_ASSIGNMENTS.get(pd.getName());
+            pd.getWriteMethod().invoke(tc, newValue);
+            
+            Template t = new Template(null, "", DEFAULT_CFG);
+            Method tReaderMethod = t.getClass().getMethod(pd.getReadMethod().getName());
+            
+            assertNotEquals("For \"" + pd.getName() + "\"", newValue, tReaderMethod.invoke(t));
+            tc.apply(t);
+            assertEquals("For \"" + pd.getName() + "\"", newValue, tReaderMethod.invoke(t));
+        }
+    }
+    
+    @Test
+    public void testConfigureCustomAttributes() throws Exception {
+        Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
+        cfg.setCustomAttribute("k1", "c");
+        cfg.setCustomAttribute("k2", "c");
+        cfg.setCustomAttribute("k3", "c");
+
+        TemplateConfiguration tc = new TemplateConfiguration();
+        tc.setCustomAttribute("k2", "tc");
+        tc.setCustomAttribute("k3", null);
+        tc.setCustomAttribute("k4", "tc");
+        tc.setCustomAttribute("k5", "tc");
+        tc.setCustomAttribute("k6", "tc");
+        CA1.set("tc", tc);
+        CA2.set("tc", tc);
+        CA3.set("tc", tc);
+
+        Template t = new Template(null, "", cfg);
+        t.setCustomAttribute("k5", "t");
+        t.setCustomAttribute("k6", null);
+        t.setCustomAttribute("k7", "t");
+        CA2.set("t", t);
+        CA3.set(null, t);
+        CA4.set("t", t);
+        
+        tc.setParentConfiguration(cfg);
+        tc.apply(t);
+        
+        assertEquals("c", t.getCustomAttribute("k1"));
+        assertEquals("tc", t.getCustomAttribute("k2"));
+        assertNull(t.getCustomAttribute("k3"));
+        assertEquals("tc", t.getCustomAttribute("k4"));
+        assertEquals("t", t.getCustomAttribute("k5"));
+        assertNull(t.getCustomAttribute("k6"));
+        assertEquals("t", t.getCustomAttribute("k7"));
+        assertEquals("tc", CA1.get(t));
+        assertEquals("t", CA2.get(t));
+        assertNull(CA3.get(t));
+        assertEquals("t", CA4.get(t));
+    }
+    
+    @Test
+    public void testConfigureParser() throws Exception {
+        Set<String> testedProps = new HashSet<String>();
+        
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+            tc.setTagSyntax(Configuration.SQUARE_BRACKET_TAG_SYNTAX);
+            assertOutputWithoutAndWithTC(tc, "[#if true]y[/#if]", "[#if true]y[/#if]", "y");
+            testedProps.add(Configuration.TAG_SYNTAX_KEY_CAMEL_CASE);
+        }
+        
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+            tc.setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
+            assertOutputWithoutAndWithTC(tc, "<#if true>y<#elseif false>n</#if>", "y", null);
+            testedProps.add(Configuration.NAMING_CONVENTION_KEY_CAMEL_CASE);
+        }
+        
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+            tc.setWhitespaceStripping(false);
+            assertOutputWithoutAndWithTC(tc, "<#if true>\nx\n</#if>\n", "x\n", "\nx\n\n");
+            testedProps.add(Configuration.WHITESPACE_STRIPPING_KEY_CAMEL_CASE);
+        }
+
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+            tc.setArithmeticEngine(new DummyArithmeticEngine());
+            assertOutputWithoutAndWithTC(tc, "${1} ${1+1}", "1 2", "11 22");
+            testedProps.add(Configuration.ARITHMETIC_ENGINE_KEY_CAMEL_CASE);
+        }
+
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+            tc.setOutputFormat(XMLOutputFormat.INSTANCE);
+            assertOutputWithoutAndWithTC(tc, "${.outputFormat} ${\"a'b\"}",
+                    UndefinedOutputFormat.INSTANCE.getName() + " a'b",
+                    XMLOutputFormat.INSTANCE.getName() + " a&apos;b");
+            testedProps.add(Configuration.OUTPUT_FORMAT_KEY_CAMEL_CASE);
+        }
+
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+            tc.setOutputFormat(XMLOutputFormat.INSTANCE);
+            tc.setAutoEscapingPolicy(Configuration.DISABLE_AUTO_ESCAPING_POLICY);
+            assertOutputWithoutAndWithTC(tc, "${'a&b'}", "a&b", "a&b");
+            testedProps.add(Configuration.AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE);
+        }
+        
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+            tc.setStrictSyntaxMode(false);
+            assertOutputWithoutAndWithTC(tc, "<if true>y</if>", "<if true>y</if>", "y");
+            testedProps.add("strictSyntaxMode");
+        }
+
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(new Configuration(new Version(2, 3, 0)));
+            assertOutputWithoutAndWithTC(tc, "<#foo>", null, "<#foo>");
+            testedProps.add(Configuration.INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE);
+        }
+
+
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(new Configuration(new Version(2, 3, 0)));
+            tc.setRecognizeStandardFileExtensions(true);
+            assertOutputWithoutAndWithTC(tc, "${.outputFormat}",
+                    UndefinedOutputFormat.INSTANCE.getName(), HTMLOutputFormat.INSTANCE.getName());
+            testedProps.add(Configuration.RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE);
+        }
+        
+        assertEquals("Check that you have tested all parser settings; ", PARSER_PROP_NAMES, testedProps);
+    }
+    
+    @Test
+    public void testConfigureParserTooLowIcI() throws Exception {
+        Configuration cfgWithTooLowIcI = new Configuration(Configuration.VERSION_2_3_21);
+        for (PropertyDescriptor propDesc : getTemplateConfigurationSettingPropDescs(true, false)) {
+            TemplateConfiguration tc = new TemplateConfiguration();
+
+            String propName = propDesc.getName();
+            Object value = SETTING_ASSIGNMENTS.get(propName);
+            propDesc.getWriteMethod().invoke(tc, value);
+            
+            boolean shouldFail;
+            if (CONFIGURABLE_PROP_NAMES.contains(propName)) {
+                shouldFail = true;
+            } else if (PARSER_PROP_NAMES.contains(propName)) {
+                shouldFail = false;
+            } else {
+                fail("Uncategorized property: " + propName);
+                return;
+            }
+            
+            try {
+                tc.setParentConfiguration(cfgWithTooLowIcI);
+                if (shouldFail) {
+                    fail("Should fail with property: " + propName);
+                }
+            } catch (IllegalStateException e) {
+                if (!shouldFail) {
+                    throw e;
+                }
+                assertThat(e.getMessage(), containsString("2.3.22"));
+            }
+        }
+    }
+    
+    @Test
+    public void testArithmeticEngine() throws TemplateException, IOException {
+        TemplateConfiguration tc = new TemplateConfiguration();
+        tc.setParentConfiguration(DEFAULT_CFG);
+        tc.setArithmeticEngine(new DummyArithmeticEngine());
+        assertOutputWithoutAndWithTC(tc,
+                "<#setting locale='en_US'>${1} ${1+1} ${1*3} <#assign x = 1>${x + x} ${x * 3}",
+                "1 2 3 2 3", "11 22 33 22 33");
+        
+        // Doesn't affect template.arithmeticEngine, only affects the parsing:
+        Template t = new Template(null, null, new StringReader(""), DEFAULT_CFG, tc, null);
+        assertEquals(DEFAULT_CFG.getArithmeticEngine(), t.getArithmeticEngine());
+    }
+
+    @Test
+    public void testStringInterpolate() throws TemplateException, IOException {
+        TemplateConfiguration tc = new TemplateConfiguration();
+        tc.setParentConfiguration(DEFAULT_CFG);
+        tc.setArithmeticEngine(new DummyArithmeticEngine());
+        assertOutputWithoutAndWithTC(tc,
+                "<#setting locale='en_US'>${'${1} ${1+1} ${1*3}'} <#assign x = 1>${'${x + x} ${x * 3}'}",
+                "1 2 3 2 3", "11 22 33 22 33");
+        
+        // Doesn't affect template.arithmeticEngine, only affects the parsing:
+        Template t = new Template(null, null, new StringReader(""), DEFAULT_CFG, tc, null);
+        assertEquals(DEFAULT_CFG.getArithmeticEngine(), t.getArithmeticEngine());
+    }
+    
+    @Test
+    public void testInterpret() throws TemplateException, IOException {
+        TemplateConfiguration tc = new TemplateConfiguration();
+        tc.setParentConfiguration(DEFAULT_CFG);
+        tc.setArithmeticEngine(new DummyArithmeticEngine());
+        assertOutputWithoutAndWithTC(tc,
+                "<#setting locale='en_US'><#assign src = r'${1} <#assign x = 1>${x + x}'><@src?interpret />",
+                "1 2", "11 22");
+        
+        tc.setWhitespaceStripping(false);
+        assertOutputWithoutAndWithTC(tc,
+                "<#if true>\nX</#if><#assign src = r'<#if true>\nY</#if>'><@src?interpret />",
+                "XY", "\nX\nY");
+    }
+
+    @Test
+    public void testEval() throws TemplateException, IOException {
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+            tc.setArithmeticEngine(new DummyArithmeticEngine());
+            assertOutputWithoutAndWithTC(tc,
+                    "<#assign x = 1>${r'1 + x'?eval?c}",
+                    "2", "22");
+            assertOutputWithoutAndWithTC(tc,
+                    "${r'1?c'?eval}",
+                    "1", "11");
+        }
+        
+        {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            tc.setParentConfiguration(DEFAULT_CFG);
+            String outputEncoding = "ISO-8859-2";
+            tc.setOutputEncoding(outputEncoding);
+
+            String legacyNCFtl = "${r'.output_encoding!\"null\"'?eval}";
+            String camelCaseNCFtl = "${r'.outputEncoding!\"null\"'?eval}";
+
+            // Default is re-auto-detecting in ?eval:
+            assertOutputWithoutAndWithTC(tc, legacyNCFtl, "null", outputEncoding);
+            assertOutputWithoutAndWithTC(tc, camelCaseNCFtl, "null", outputEncoding);
+            
+            // Force camelCase:
+            tc.setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
+            assertOutputWithoutAndWithTC(tc, legacyNCFtl, "null", null);
+            assertOutputWithoutAndWithTC(tc, camelCaseNCFtl, "null", outputEncoding);
+            
+            // Force legacy:
+            tc.setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
+            assertOutputWithoutAndWithTC(tc, legacyNCFtl, "null", outputEncoding);
+            assertOutputWithoutAndWithTC(tc, camelCaseNCFtl, "null", null);
+        }
+    }
+    
+    @Test
+    public void testSetParentConfiguration() throws IOException {
+        TemplateConfiguration tc = new TemplateConfiguration();
+        
+        Template t = new Template(null, "", DEFAULT_CFG);
+        try {
+            tc.apply(t);
+            fail();
+        } catch (IllegalStateException e) {
+            assertThat(e.getMessage(), containsString("Configuration"));
+        }
+        
+        tc.setParent(DEFAULT_CFG);
+        
+        try {
+            tc.setParentConfiguration(new Configuration());
+            fail();
+        } catch (IllegalStateException e) {
+            assertThat(e.getMessage(), containsString("Configuration"));
+        }
+
+        try {
+            // Same as setParentConfiguration
+            tc.setParent(new Configuration());
+            fail();
+        } catch (IllegalStateException e) {
+            assertThat(e.getMessage(), containsString("Configuration"));
+        }
+        
+        try {
+            tc.setParentConfiguration(null);
+            fail();
+        } catch (NullArgumentException e) {
+            // exected
+        }
+        
+        tc.setParent(DEFAULT_CFG);
+        
+        tc.apply(t);
+    }
+    
+    private void assertOutputWithoutAndWithTC(TemplateConfiguration tc, String ftl, String expectedDefaultOutput,
+            String expectedConfiguredOutput) throws TemplateException, IOException {
+        assertOutput(tc, ftl, expectedConfiguredOutput);
+        assertOutput(null, ftl, expectedDefaultOutput);
+    }
+
+    private void assertOutput(TemplateConfiguration tc, String ftl, String expectedConfiguredOutput)
+            throws TemplateException, IOException {
+        StringWriter sw = new StringWriter();
+        try {
+            Configuration cfg = tc != null ? tc.getParentConfiguration() : DEFAULT_CFG;
+            Template t = new Template("adhoc.ftlh", null, new StringReader(ftl), cfg, tc, null);
+            if (tc != null) {
+                tc.apply(t);
+            }
+            t.process(null, sw);
+            if (expectedConfiguredOutput == null) {
+                fail("Template should have fail.");
+            }
+        } catch (TemplateException e) {
+            if (expectedConfiguredOutput != null) {
+                throw e;
+            }
+        } catch (ParseException e) {
+            if (expectedConfiguredOutput != null) {
+                throw e;
+            }
+        }
+        if (expectedConfiguredOutput != null) {
+            assertEquals(expectedConfiguredOutput, sw.toString());
+        }
+    }
+
+    @Test
+    public void testIsSet() throws Exception {
+        for (PropertyDescriptor pd : getTemplateConfigurationSettingPropDescs(true, true)) {
+            TemplateConfiguration tc = new TemplateConfiguration();
+            checkAllIsSetFalseExcept(tc, null);
+            pd.getWriteMethod().invoke(tc, SETTING_ASSIGNMENTS.get(pd.getName()));
+            checkAllIsSetFalseExcept(tc, pd.getName());
+        }
+    }
+
+    private void checkAllIsSetFalseExcept(TemplateConfiguration tc, String setSetting)
+            throws SecurityException, IntrospectionException,
+            IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+        for (PropertyDescriptor pd : getTemplateConfigurationSettingPropDescs(true, true)) {
+            String isSetMethodName = getIsSetMethodName(pd.getReadMethod().getName());
+            Method isSetMethod;
+            try {
+                isSetMethod = TemplateConfiguration.class.getMethod(isSetMethodName);
+            } catch (NoSuchMethodException e) {
+                fail("Missing " + isSetMethodName + " method for \"" + pd.getName() + "\".");
+                return;
+            }
+            if (pd.getName().equals(setSetting)) {
+                assertTrue(isSetMethod + " should return true", (Boolean) (isSetMethod.invoke(tc)));
+            } else {
+                assertFalse(isSetMethod + " should return false", (Boolean) (isSetMethod.invoke(tc)));
+            }
+        }
+    }
+
+    /**
+     * Test case self-check.
+     */
+    @Test
+    public void checkTestAssignments() throws Exception {
+        for (PropertyDescriptor pd : getTemplateConfigurationSettingPropDescs(true, true)) {
+            String propName = pd.getName();
+            if (!SETTING_ASSIGNMENTS.containsKey(propName)) {
+                fail("Test case doesn't cover all settings in SETTING_ASSIGNMENTS. Missing: " + propName);
+            }
+            Method readMethod = pd.getReadMethod();
+            String cfgMethodName = readMethod.getName();
+            if (cfgMethodName.equals("getEncoding")) {
+                // Because Configuration has local-to-encoding map too, this has a different name there.
+                cfgMethodName = "getDefaultEncoding";
+            }
+            Method cfgMethod = DEFAULT_CFG.getClass().getMethod(cfgMethodName, readMethod.getParameterTypes());
+            Object defaultSettingValue = cfgMethod.invoke(DEFAULT_CFG);
+            Object assignedValue = SETTING_ASSIGNMENTS.get(propName);
+            assertNotEquals("SETTING_ASSIGNMENTS must contain a non-default value for " + propName,
+                    assignedValue, defaultSettingValue);
+
+            TemplateConfiguration tc = new TemplateConfiguration();
+            try {
+                pd.getWriteMethod().invoke(tc, assignedValue);
+            } catch (Exception e) {
+                throw new IllegalStateException("For setting \"" + propName + "\" and assigned value of type "
+                        + (assignedValue != null ? assignedValue.getClass().getName() : "Null"),
+                        e);
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/core/TemplateConfigurationWithTemplateCacheTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/TemplateConfigurationWithTemplateCacheTest.java b/src/test/java/freemarker/core/TemplateConfigurationWithTemplateCacheTest.java
new file mode 100644
index 0000000..611c2e3
--- /dev/null
+++ b/src/test/java/freemarker/core/TemplateConfigurationWithTemplateCacheTest.java
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.core;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+
+import org.junit.Test;
+
+import freemarker.cache.ByteArrayTemplateLoader;
+import freemarker.cache.ConditionalTemplateConfigurationFactory;
+import freemarker.cache.FileNameGlobMatcher;
+import freemarker.cache.FirstMatchTemplateConfigurationFactory;
+import freemarker.cache.MergingTemplateConfigurationFactory;
+import freemarker.cache.StringTemplateLoader;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+public class TemplateConfigurationWithTemplateCacheTest {
+
+    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_2_3_23);
+        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_2_3_22);
+        
+        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_2_3_22);
+        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_2_3_22);
+        
+        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_2_3_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;
+    }
+
+}


[6/7] incubator-freemarker git commit: The new (in 2.3.24-pre01) TemplateConfigurer class was renamed to TemplateConfiguration, and the related configuration setting from template_configurers to template_configurations. Also, the TemplateConfigurer.confi

Posted by dd...@apache.org.
The new (in 2.3.24-pre01) TemplateConfigurer class was renamed to TemplateConfiguration, and the related configuration setting from template_configurers to template_configurations. Also, the TemplateConfigurer.configure method was renamed to TemplateConfiguration.apply. Also did some JavaDoc typo fixes and additions for the related API-s.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/ea5c47d1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/ea5c47d1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/ea5c47d1

Branch: refs/heads/2.3-gae
Commit: ea5c47d13366ccf8e90d69ddc94bdfa911719b77
Parents: 159bcb2
Author: ddekany <dd...@apache.org>
Authored: Sat Dec 19 17:36:27 2015 +0100
Committer: ddekany <dd...@apache.org>
Committed: Sat Dec 19 17:40:52 2015 +0100

----------------------------------------------------------------------
 ...ConditionalTemplateConfigurationFactory.java |  76 ++
 .../ConditionalTemplateConfigurerFactory.java   |  75 --
 .../FirstMatchTemplateConfigurationFactory.java | 118 +++
 .../FirstMatchTemplateConfigurerFactory.java    | 118 ---
 .../MergingTemplateConfigurationFactory.java    |  78 ++
 .../cache/MergingTemplateConfigurerFactory.java |  78 --
 .../java/freemarker/cache/TemplateCache.java    |  28 +-
 .../cache/TemplateConfigurationFactory.java     |  91 ++
 .../TemplateConfigurationFactoryException.java  |  36 +
 .../cache/TemplateConfigurerFactory.java        |  91 --
 .../TemplateConfigurerFactoryException.java     |  36 -
 src/main/java/freemarker/core/Configurable.java |  16 +-
 .../java/freemarker/core/CustomAttribute.java   |  12 +-
 .../freemarker/core/TemplateConfiguration.java  | 578 +++++++++++++
 .../freemarker/core/TemplateConfigurer.java     | 580 -------------
 .../freemarker/core/UndefinedOutputFormat.java  |   6 +-
 .../core/_ObjectBuilderSettingEvaluator.java    |  14 +-
 .../ext/servlet/FreemarkerServlet.java          |   2 +-
 .../java/freemarker/template/Configuration.java | 102 +--
 src/main/java/freemarker/template/Template.java |  30 +-
 src/manual/book.xml                             | 199 ++---
 .../cache/TemplateConfigurationFactoryTest.java | 230 +++++
 .../cache/TemplateConfigurerFactoryTest.java    | 230 -----
 .../java/freemarker/core/DateFormatTest.java    |   6 +-
 .../java/freemarker/core/NumberFormatTest.java  |   6 +-
 .../java/freemarker/core/OutputFormatTest.java  |  42 +-
 .../core/TemplateConfigurationTest.java         | 846 +++++++++++++++++++
 ...plateConfigurationWithTemplateCacheTest.java | 325 +++++++
 .../freemarker/core/TemplateConfigurerTest.java | 846 -------------------
 ...TemplateConfigurerWithTemplateCacheTest.java | 325 -------
 .../ext/servlet/FreemarkerServletTest.java      |  20 +-
 .../manual/ConfigureOutputFormatExamples.java   |  32 +-
 .../manual/TemplateConfigurationExamples.java   | 186 ++++
 .../manual/TemplateConfigurerExamples.java      | 186 ----
 .../freemarker/template/ConfigurationTest.java  |  41 +-
 .../ConfigureOutputFormatExamples1.properties   |   6 +-
 .../ConfigureOutputFormatExamples2.properties   |  16 +-
 .../TemplateConfigurationExamples1.properties   |   8 +
 .../TemplateConfigurationExamples2.properties   |  15 +
 .../TemplateConfigurationExamples3.properties   |  30 +
 .../TemplateConfigurerExamples1.properties      |   8 -
 .../TemplateConfigurerExamples2.properties      |  15 -
 .../TemplateConfigurerExamples3.properties      |  30 -
 43 files changed, 2911 insertions(+), 2902 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/ConditionalTemplateConfigurationFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/ConditionalTemplateConfigurationFactory.java b/src/main/java/freemarker/cache/ConditionalTemplateConfigurationFactory.java
new file mode 100644
index 0000000..430632e
--- /dev/null
+++ b/src/main/java/freemarker/cache/ConditionalTemplateConfigurationFactory.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.cache;
+
+import java.io.IOException;
+
+import freemarker.core.TemplateConfiguration;
+import freemarker.template.Configuration;
+
+/**
+ * Returns the given {@link TemplateConfiguration} directly, or another {@link TemplateConfigurationFactory}'s result, when
+ * the specified matcher matches the template source.
+ * 
+ * @since 2.3.24
+ */
+public class ConditionalTemplateConfigurationFactory extends TemplateConfigurationFactory {
+
+    private final TemplateSourceMatcher matcher;
+    private final TemplateConfiguration templateConfiguration;
+    private final TemplateConfigurationFactory templateConfigurationFactory;
+
+    public ConditionalTemplateConfigurationFactory(
+            TemplateSourceMatcher matcher, TemplateConfigurationFactory templateConfigurationFactory) {
+        this.matcher = matcher;
+        this.templateConfiguration = null;
+        this.templateConfigurationFactory = templateConfigurationFactory;
+    }
+    
+    public ConditionalTemplateConfigurationFactory(
+            TemplateSourceMatcher matcher, TemplateConfiguration templateConfiguration) {
+        this.matcher = matcher;
+        this.templateConfiguration = templateConfiguration;
+        this.templateConfigurationFactory = null;
+    }
+
+    @Override
+    public TemplateConfiguration get(String sourceName, Object templateSource)
+            throws IOException, TemplateConfigurationFactoryException {
+        if (matcher.matches(sourceName, templateSource)) {
+            if (templateConfigurationFactory != null) {
+                return templateConfigurationFactory.get(sourceName, templateSource);
+            } else {
+                return templateConfiguration;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected void setConfigurationOfChildren(Configuration cfg) {
+        if (templateConfiguration != null) {
+            templateConfiguration.setParentConfiguration(cfg);
+        }
+        if (templateConfigurationFactory != null) {
+            templateConfigurationFactory.setConfiguration(cfg);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/ConditionalTemplateConfigurerFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/ConditionalTemplateConfigurerFactory.java b/src/main/java/freemarker/cache/ConditionalTemplateConfigurerFactory.java
deleted file mode 100644
index 1982951..0000000
--- a/src/main/java/freemarker/cache/ConditionalTemplateConfigurerFactory.java
+++ /dev/null
@@ -1,75 +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 freemarker.cache;
-
-import java.io.IOException;
-
-import freemarker.core.TemplateConfigurer;
-import freemarker.template.Configuration;
-
-/**
- * Returns the given {@link TemplateConfigurer} directly, or another {@link TemplateConfigurerFactory}'s result, when
- * the specified matcher matches the template source.
- * 
- * @since 2.3.24
- */
-public class ConditionalTemplateConfigurerFactory extends TemplateConfigurerFactory {
-
-    private final TemplateSourceMatcher matcher;
-    private final TemplateConfigurer templateConfigurer;
-    private final TemplateConfigurerFactory templateConfigurerFactory;
-
-    public ConditionalTemplateConfigurerFactory(
-            TemplateSourceMatcher matcher, TemplateConfigurerFactory templateConfigurerFactory) {
-        this.matcher = matcher;
-        this.templateConfigurer = null;
-        this.templateConfigurerFactory = templateConfigurerFactory;
-    }
-    
-    public ConditionalTemplateConfigurerFactory(TemplateSourceMatcher matcher, TemplateConfigurer templateConfigurer) {
-        this.matcher = matcher;
-        this.templateConfigurer = templateConfigurer;
-        this.templateConfigurerFactory = null;
-    }
-
-    @Override
-    public TemplateConfigurer get(String sourceName, Object templateSource)
-            throws IOException, TemplateConfigurerFactoryException {
-        if (matcher.matches(sourceName, templateSource)) {
-            if (templateConfigurerFactory != null) {
-                return templateConfigurerFactory.get(sourceName, templateSource);
-            } else {
-                return templateConfigurer;
-            }
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    protected void setConfigurationOfChildren(Configuration cfg) {
-        if (templateConfigurer != null) {
-            templateConfigurer.setParentConfiguration(cfg);
-        }
-        if (templateConfigurerFactory != null) {
-            templateConfigurerFactory.setConfiguration(cfg);
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/FirstMatchTemplateConfigurationFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/FirstMatchTemplateConfigurationFactory.java b/src/main/java/freemarker/cache/FirstMatchTemplateConfigurationFactory.java
new file mode 100644
index 0000000..9382656
--- /dev/null
+++ b/src/main/java/freemarker/cache/FirstMatchTemplateConfigurationFactory.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.cache;
+
+import java.io.IOException;
+
+import freemarker.core.TemplateConfiguration;
+import freemarker.template.Configuration;
+import freemarker.template.utility.StringUtil;
+
+/**
+ * Returns the first non-{@code null} result of the child factories, ignoring all further child factories. The child
+ * factories are called in the order as they were added.
+ */
+public class FirstMatchTemplateConfigurationFactory extends TemplateConfigurationFactory {
+    
+    private final TemplateConfigurationFactory[] templateConfigurationFactories;
+    private boolean allowNoMatch;
+    private String noMatchErrorDetails;
+    
+    public FirstMatchTemplateConfigurationFactory(TemplateConfigurationFactory... templateConfigurationFactories) {
+        this.templateConfigurationFactories = templateConfigurationFactories;
+    }
+
+    @Override
+    public TemplateConfiguration get(String sourceName, Object templateSource)
+            throws IOException, TemplateConfigurationFactoryException {
+        for (TemplateConfigurationFactory tcf : templateConfigurationFactories) {
+            TemplateConfiguration tc = tcf.get(sourceName, templateSource); 
+            if (tc != null) {
+                return tc;
+            }
+        }
+        if (!allowNoMatch) {
+            throw new TemplateConfigurationFactoryException(
+                    FirstMatchTemplateConfigurationFactory.class.getSimpleName()
+                    + " has found no matching choice for source name "
+                    + StringUtil.jQuote(sourceName) + ". "
+                    + (noMatchErrorDetails != null
+                            ? "Error details: " + noMatchErrorDetails 
+                            : "(Set the noMatchErrorDetails property of the factory bean to give a more specific error "
+                                    + "message. Set allowNoMatch to true if this shouldn't be an error.)"));
+        }
+        return null;
+    }
+
+    /**
+     * Getter pair of {@link #setAllowNoMatch(boolean)}.
+     */
+    public boolean getAllowNoMatch() {
+        return allowNoMatch;
+    }
+
+    /**
+     * Use this to specify if having no matching choice is an error. The default is {@code false}, that is, it's an
+     * error if there was no matching choice.
+     * 
+     * @see #setNoMatchErrorDetails(String)
+     */
+    public void setAllowNoMatch(boolean allowNoMatch) {
+        this.allowNoMatch = allowNoMatch;
+    }
+
+    /**
+     * Use this to specify the text added to the exception error message when there was no matching choice.
+     * The default is {@code null} (no error details).
+     * 
+     * @see #setAllowNoMatch(boolean)
+     */
+    public String getNoMatchErrorDetails() {
+        return noMatchErrorDetails;
+    }
+
+    
+    public void setNoMatchErrorDetails(String noMatchErrorDetails) {
+        this.noMatchErrorDetails = noMatchErrorDetails;
+    }
+    
+    /**
+     * Same as {@link #setAllowNoMatch(boolean)}, but return this object to support "fluent API" style. 
+     */
+    public FirstMatchTemplateConfigurationFactory allowNoMatch(boolean allow) {
+        setAllowNoMatch(allow);
+        return this;
+    }
+
+    /**
+     * Same as {@link #setNoMatchErrorDetails(String)}, but return this object to support "fluent API" style. 
+     */
+    public FirstMatchTemplateConfigurationFactory noMatchErrorDetails(String message) {
+        setNoMatchErrorDetails(message);
+        return this;
+    }
+
+    @Override
+    protected void setConfigurationOfChildren(Configuration cfg) {
+        for (TemplateConfigurationFactory templateConfigurationFactory : templateConfigurationFactories) {
+            templateConfigurationFactory.setConfiguration(cfg);
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/FirstMatchTemplateConfigurerFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/FirstMatchTemplateConfigurerFactory.java b/src/main/java/freemarker/cache/FirstMatchTemplateConfigurerFactory.java
deleted file mode 100644
index 77512ff..0000000
--- a/src/main/java/freemarker/cache/FirstMatchTemplateConfigurerFactory.java
+++ /dev/null
@@ -1,118 +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 freemarker.cache;
-
-import java.io.IOException;
-
-import freemarker.core.TemplateConfigurer;
-import freemarker.template.Configuration;
-import freemarker.template.utility.StringUtil;
-
-/**
- * Returns the first non-{@code null} result of the child factories, ignoring all further child factories. The child
- * factories are called in the order as they were added.
- */
-public class FirstMatchTemplateConfigurerFactory extends TemplateConfigurerFactory {
-    
-    private final TemplateConfigurerFactory[] templateConfigurerFactories;
-    private boolean allowNoMatch;
-    private String noMatchErrorDetails;
-    
-    public FirstMatchTemplateConfigurerFactory(TemplateConfigurerFactory... templateConfigurerFactories) {
-        this.templateConfigurerFactories = templateConfigurerFactories;
-    }
-
-    @Override
-    public TemplateConfigurer get(String sourceName, Object templateSource)
-            throws IOException, TemplateConfigurerFactoryException {
-        for (TemplateConfigurerFactory tcf : templateConfigurerFactories) {
-            TemplateConfigurer tc = tcf.get(sourceName, templateSource); 
-            if (tc != null) {
-                return tc;
-            }
-        }
-        if (!allowNoMatch) {
-            throw new TemplateConfigurerFactoryException(
-                    FirstMatchTemplateConfigurerFactory.class.getSimpleName()
-                    + " has found no matching choice for source name "
-                    + StringUtil.jQuote(sourceName) + ". "
-                    + (noMatchErrorDetails != null
-                            ? "Error details: " + noMatchErrorDetails 
-                            : "(Set the noMatchErrorDetails property of the factory bean to give a more specific error "
-                                    + "message. Set allowNoMatch to true if this shouldn't be an error.)"));
-        }
-        return null;
-    }
-
-    /**
-     * Getter pair of {@link #setAllowNoMatch(boolean)}.
-     */
-    public boolean getAllowNoMatch() {
-        return allowNoMatch;
-    }
-
-    /**
-     * Use this to specify if having no matching choice is an error. The default is {@code false}, that is, it's an
-     * error if there was no matching choice.
-     * 
-     * @see #setNoMatchErrorDetails(String)
-     */
-    public void setAllowNoMatch(boolean allowNoMatch) {
-        this.allowNoMatch = allowNoMatch;
-    }
-
-    /**
-     * Use this to specify the text added to the exception error message when there was no matching choice.
-     * The default is {@code null} (no error details).
-     * 
-     * @see #setAllowNoMatch(boolean)
-     */
-    public String getNoMatchErrorDetails() {
-        return noMatchErrorDetails;
-    }
-
-    
-    public void setNoMatchErrorDetails(String noMatchErrorDetails) {
-        this.noMatchErrorDetails = noMatchErrorDetails;
-    }
-    
-    /**
-     * Same as {@link #setAllowNoMatch(boolean)}, but return this object to support "fluent API" style. 
-     */
-    public FirstMatchTemplateConfigurerFactory allowNoMatch(boolean allow) {
-        setAllowNoMatch(allow);
-        return this;
-    }
-
-    /**
-     * Same as {@link #setNoMatchErrorDetails(String)}, but return this object to support "fluent API" style. 
-     */
-    public FirstMatchTemplateConfigurerFactory noMatchErrorDetails(String message) {
-        setNoMatchErrorDetails(message);
-        return this;
-    }
-
-    @Override
-    protected void setConfigurationOfChildren(Configuration cfg) {
-        for (TemplateConfigurerFactory templateConfigurerFactory : templateConfigurerFactories) {
-            templateConfigurerFactory.setConfiguration(cfg);
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/MergingTemplateConfigurationFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/MergingTemplateConfigurationFactory.java b/src/main/java/freemarker/cache/MergingTemplateConfigurationFactory.java
new file mode 100644
index 0000000..3675d19
--- /dev/null
+++ b/src/main/java/freemarker/cache/MergingTemplateConfigurationFactory.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.cache;
+
+import java.io.IOException;
+
+import freemarker.core.TemplateConfiguration;
+import freemarker.template.Configuration;
+
+/**
+ * Returns the merged results of all the child factories. The factories are merged in the order as they were added.
+ * {@code null} results from the child factories will be ignored. If all child factories return {@code null}, the result
+ * of this factory will be {@code null} too.
+ * 
+ * @since 2.3.24
+ */
+public class MergingTemplateConfigurationFactory extends TemplateConfigurationFactory {
+    
+    private final TemplateConfigurationFactory[] templateConfigurationFactories;
+    
+    public MergingTemplateConfigurationFactory(TemplateConfigurationFactory... templateConfigurationFactories) {
+        this.templateConfigurationFactories = templateConfigurationFactories;
+    }
+
+    @Override
+    public TemplateConfiguration get(String sourceName, Object templateSource)
+            throws IOException, TemplateConfigurationFactoryException {
+        TemplateConfiguration mergedTC = null;
+        TemplateConfiguration resultTC = null;
+        for (TemplateConfigurationFactory tcf : templateConfigurationFactories) {
+            TemplateConfiguration tc = tcf.get(sourceName, templateSource);
+            if (tc != null) {
+                if (resultTC == null) {
+                    resultTC = tc;
+                } else {
+                    if (mergedTC == null) {
+                        Configuration cfg = getConfiguration();
+                        if (cfg == null) {
+                            throw new IllegalStateException(
+                                    "The TemplateConfigurationFactory wasn't associated to a Configuration yet.");
+                        }
+                        
+                        mergedTC = new TemplateConfiguration();
+                        mergedTC.setParentConfiguration(cfg);
+                        mergedTC.merge(resultTC);
+                        resultTC = mergedTC;
+                    }
+                    mergedTC.merge(tc);
+                }
+            }
+        }
+        return resultTC;
+    }
+    
+    @Override
+    protected void setConfigurationOfChildren(Configuration cfg) {
+        for (TemplateConfigurationFactory templateConfigurationFactory : templateConfigurationFactories) {
+            templateConfigurationFactory.setConfiguration(cfg);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/MergingTemplateConfigurerFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/MergingTemplateConfigurerFactory.java b/src/main/java/freemarker/cache/MergingTemplateConfigurerFactory.java
deleted file mode 100644
index 4a29614..0000000
--- a/src/main/java/freemarker/cache/MergingTemplateConfigurerFactory.java
+++ /dev/null
@@ -1,78 +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 freemarker.cache;
-
-import java.io.IOException;
-
-import freemarker.core.TemplateConfigurer;
-import freemarker.template.Configuration;
-
-/**
- * Returns the merged results of all the child factories. The factories are merged in the order as they were added.
- * {@code null} results from the child factories will be ignored. If all child factories return {@code null}, the result
- * of this factory will be {@code null} too.
- * 
- * @since 2.3.24
- */
-public class MergingTemplateConfigurerFactory extends TemplateConfigurerFactory {
-    
-    private final TemplateConfigurerFactory[] templateConfigurerFactories;
-    
-    public MergingTemplateConfigurerFactory(TemplateConfigurerFactory... templateConfigurerFactories) {
-        this.templateConfigurerFactories = templateConfigurerFactories;
-    }
-
-    @Override
-    public TemplateConfigurer get(String sourceName, Object templateSource)
-            throws IOException, TemplateConfigurerFactoryException {
-        TemplateConfigurer mergedTC = null;
-        TemplateConfigurer resultTC = null;
-        for (TemplateConfigurerFactory tcf : templateConfigurerFactories) {
-            TemplateConfigurer tc = tcf.get(sourceName, templateSource);
-            if (tc != null) {
-                if (resultTC == null) {
-                    resultTC = tc;
-                } else {
-                    if (mergedTC == null) {
-                        Configuration cfg = getConfiguration();
-                        if (cfg == null) {
-                            throw new IllegalStateException(
-                                    "The TemplateConfigurerFactory wasn't associated to a Configuration yet.");
-                        }
-                        
-                        mergedTC = new TemplateConfigurer();
-                        mergedTC.setParentConfiguration(cfg);
-                        mergedTC.merge(resultTC);
-                        resultTC = mergedTC;
-                    }
-                    mergedTC.merge(tc);
-                }
-            }
-        }
-        return resultTC;
-    }
-    
-    @Override
-    protected void setConfigurationOfChildren(Configuration cfg) {
-        for (TemplateConfigurerFactory templateConfigurerFactory : templateConfigurerFactories) {
-            templateConfigurerFactory.setConfiguration(cfg);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/TemplateCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/TemplateCache.java b/src/main/java/freemarker/cache/TemplateCache.java
index 1775f17..4f62d05 100644
--- a/src/main/java/freemarker/cache/TemplateCache.java
+++ b/src/main/java/freemarker/cache/TemplateCache.java
@@ -33,7 +33,7 @@ import java.util.StringTokenizer;
 import freemarker.cache.MultiTemplateLoader.MultiSource;
 import freemarker.core.BugException;
 import freemarker.core.Environment;
-import freemarker.core.TemplateConfigurer;
+import freemarker.core.TemplateConfiguration;
 import freemarker.log.Logger;
 import freemarker.template.Configuration;
 import freemarker.template.MalformedTemplateNameException;
@@ -77,7 +77,7 @@ public class TemplateCache {
     private final CacheStorage storage;
     private final TemplateLookupStrategy templateLookupStrategy;
     private final TemplateNameFormat templateNameFormat;
-    private final TemplateConfigurerFactory templateConfigurers;
+    private final TemplateConfigurationFactory templateConfigurations;
     
     private final boolean isStorageConcurrent;
     /** {@link Configuration#setTemplateUpdateDelayMilliseconds(long)} */
@@ -143,7 +143,7 @@ public class TemplateCache {
     /**
      * Same as
      * {@link TemplateCache#TemplateCache(TemplateLoader, CacheStorage, TemplateLookupStrategy, TemplateNameFormat,
-     * TemplateConfigurerFactory, Configuration)} with {@code null} for {@code templateConfigurer}-s.
+     * TemplateConfigurationFactory, Configuration)} with {@code null} for {@code templateConfigurations}-s.
      * 
      * @since 2.3.22
      */
@@ -162,8 +162,8 @@ public class TemplateCache {
      *            The {@link TemplateLookupStrategy} to use. Can't be {@code null}.
      * @param templateNameFormat
      *            The {@link TemplateNameFormat} to use. Can't be {@code null}.
-     * @param templateConfigurers
-     *            The {@link TemplateConfigurerFactory} to use. Can be {@code null} (then all templates will use the
+     * @param templateConfigurations
+     *            The {@link TemplateConfigurationFactory} to use. Can be {@code null} (then all templates will use the
      *            settings coming from the {@link Configuration} as is).
      * @param config
      *            The {@link Configuration} this cache will be used for. Can be {@code null} for backward compatibility,
@@ -173,7 +173,7 @@ public class TemplateCache {
      */
     public TemplateCache(TemplateLoader templateLoader, CacheStorage cacheStorage,
             TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat templateNameFormat,
-            TemplateConfigurerFactory templateConfigurers,
+            TemplateConfigurationFactory templateConfigurations,
             Configuration config) {
         this.templateLoader = templateLoader;
         
@@ -189,7 +189,7 @@ public class TemplateCache {
         this.templateNameFormat = templateNameFormat;
 
         // Can be null
-        this.templateConfigurers = templateConfigurers;
+        this.templateConfigurations = templateConfigurations;
         
         this.config = config;
     }
@@ -232,8 +232,8 @@ public class TemplateCache {
     /**
      * @since 2.3.24
      */
-    public TemplateConfigurerFactory getTemplateConfigurers() {
-        return templateConfigurers;
+    public TemplateConfigurationFactory getTemplateConfigurations() {
+        return templateConfigurations;
     }
 
     /**
@@ -523,11 +523,11 @@ public class TemplateCache {
             final TemplateLoader templateLoader, final Object source,
             final String name, final String sourceName, Locale locale, final Object customLookupCondition,
             String initialEncoding, final boolean parseAsFTL) throws IOException {
-        final TemplateConfigurer tc;
+        final TemplateConfiguration tc;
         try {
-            tc = templateConfigurers != null ? templateConfigurers.get(sourceName, source) : null;
-        } catch (TemplateConfigurerFactoryException e) {
-            throw newIOException("Error while getting TemplateConfigurer; see cause exception.", e);
+            tc = templateConfigurations != null ? templateConfigurations.get(sourceName, source) : null;
+        } catch (TemplateConfigurationFactoryException e) {
+            throw newIOException("Error while getting TemplateConfiguration; see cause exception.", e);
         }
         if (tc != null) {
             // TC.{encoding,locale} is stronger than the cfg.getTemplate arguments by design.
@@ -586,7 +586,7 @@ public class TemplateCache {
         }
 
         if (tc != null) {
-            tc.configure(template);
+            tc.apply(template);
         }
         
         template.setLocale(locale);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/TemplateConfigurationFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/TemplateConfigurationFactory.java b/src/main/java/freemarker/cache/TemplateConfigurationFactory.java
new file mode 100644
index 0000000..ec8d2ae
--- /dev/null
+++ b/src/main/java/freemarker/cache/TemplateConfigurationFactory.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.cache;
+
+import java.io.IOException;
+
+import freemarker.core.TemplateConfiguration;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+
+/**
+ * Creates (or returns) {@link TemplateConfiguration}-s for template sources.
+ * 
+ * @since 2.3.24
+ */
+public abstract class TemplateConfigurationFactory {
+    
+    private Configuration cfg;
+
+    /**
+     * Returns (maybe creates) the {@link TemplateConfiguration} for the given template source.
+     * 
+     * @param sourceName
+     *            The name (path) that was used for {@link TemplateLoader#findTemplateSource(String)}. See
+     *            {@link Template#getSourceName()} for details.
+     * @param templateSource
+     *            The object returned by {@link TemplateLoader#findTemplateSource(String)}.
+     * 
+     * @return The {@link TemplateConfiguration} to apply, or {@code null} if the there's no {@link TemplateConfiguration} for
+     *         this template source.
+     * 
+     * @throws IOException
+     *             Typically, if there factory needs further I/O to find out more about the template source, but that
+     *             fails.
+     * @throws TemplateConfigurationFactoryException
+     *             If there's a problem that's specific to the factory logic.
+     */
+    public abstract TemplateConfiguration get(String sourceName, Object templateSource)
+            throws IOException, TemplateConfigurationFactoryException;
+    
+    /**
+     * Binds this {@link TemplateConfigurationFactory} to a {@link Configuration}. Once it's bound, it can't be bound to
+     * another {@link Configuration} any more. This is automatically called by
+     * {@link Configuration#setTemplateConfigurations(TemplateConfigurationFactory)}.
+     */
+    public final void setConfiguration(Configuration cfg) {
+        if (this.cfg != null) {
+            if (cfg != this.cfg) {
+                throw new IllegalStateException(
+                        "The TemplateConfigurationFactory is already bound to another Configuration");
+            }
+            return;
+        } else {
+            this.cfg = cfg;
+            setConfigurationOfChildren(cfg);
+        }
+    }
+    
+    /**
+     * Returns the configuration this object belongs to, or {@code null} if it isn't yet bound to a
+     * {@link Configuration}.
+     */
+    public Configuration getConfiguration() {
+        return cfg;
+    }
+    
+    /**
+     * Calls {@link TemplateConfiguration#setParentConfiguration(Configuration)} on each enclosed
+     * {@link TemplateConfiguration} and {@link TemplateConfigurationFactory#setConfiguration(Configuration)}
+     * on each enclosed {@link TemplateConfigurationFactory} objects. It only supposed to call these on the direct
+     * "children" of this object, not on the children of the children.
+     */
+    protected abstract void setConfigurationOfChildren(Configuration cfg);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/TemplateConfigurationFactoryException.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/TemplateConfigurationFactoryException.java b/src/main/java/freemarker/cache/TemplateConfigurationFactoryException.java
new file mode 100644
index 0000000..abbce62
--- /dev/null
+++ b/src/main/java/freemarker/cache/TemplateConfigurationFactoryException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.cache;
+
+/**
+ * Non-I/O exception thrown by {@link TemplateConfigurationFactory}-s.  
+ * 
+ * @since 2.3.24
+ */
+public class TemplateConfigurationFactoryException extends Exception {
+
+    public TemplateConfigurationFactoryException(String message) {
+        super(message);
+    }
+
+    public TemplateConfigurationFactoryException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/TemplateConfigurerFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/TemplateConfigurerFactory.java b/src/main/java/freemarker/cache/TemplateConfigurerFactory.java
deleted file mode 100644
index 0e65a67..0000000
--- a/src/main/java/freemarker/cache/TemplateConfigurerFactory.java
+++ /dev/null
@@ -1,91 +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 freemarker.cache;
-
-import java.io.IOException;
-
-import freemarker.core.TemplateConfigurer;
-import freemarker.template.Configuration;
-import freemarker.template.Template;
-
-/**
- * Creates (or returns) {@link TemplateConfigurer}-s for template sources.
- * 
- * @since 2.3.24
- */
-public abstract class TemplateConfigurerFactory {
-    
-    private Configuration cfg;
-
-    /**
-     * Returns (maybe creates) the {@link TemplateConfigurer} for the given template source.
-     * 
-     * @param sourceName
-     *            The name (path) that was used for {@link TemplateLoader#findTemplateSource(String)}. See
-     *            {@link Template#getSourceName()} for details.
-     * @param templateSource
-     *            The object returned by {@link TemplateLoader#findTemplateSource(String)}.
-     * 
-     * @return The {@link TemplateConfigurer} to apply, or {@code null} if the there's no {@link TemplateConfigurer} for
-     *         this template source.
-     * 
-     * @throws IOException
-     *             Typically, if there factory needs further I/O to find out more about the template source, but that
-     *             fails.
-     * @throws TemplateConfigurerFactoryException
-     *             If there's a problem that's specific to the factory logic.
-     */
-    public abstract TemplateConfigurer get(String sourceName, Object templateSource)
-            throws IOException, TemplateConfigurerFactoryException;
-    
-    /**
-     * Binds this {@link TemplateConfigurerFactory} to a {@link Configuration}. Once it's bound, it can't be bound to
-     * another {@link Configuration} any more. This is automatically called by
-     * {@link Configuration#setTemplateConfigurers(TemplateConfigurerFactory)}.
-     */
-    public final void setConfiguration(Configuration cfg) {
-        if (this.cfg != null) {
-            if (cfg != this.cfg) {
-                throw new IllegalStateException(
-                        "The TemplateConfigurerFactory is already bound to another Configuration");
-            }
-            return;
-        } else {
-            this.cfg = cfg;
-            setConfigurationOfChildren(cfg);
-        }
-    }
-    
-    /**
-     * Returns the configuration this object belongs to, or {@code null} if it isn't yet bound to a
-     * {@link Configuration}.
-     */
-    public Configuration getConfiguration() {
-        return cfg;
-    }
-    
-    /**
-     * Calls {@link TemplateConfigurer#setParentConfiguration(Configuration)} on each enclosed
-     * {@link TemplateConfigurer} and {@link TemplateConfigurerFactory#setConfiguration(Configuration)}
-     * on each enclosed {@link TemplateConfigurerFactory} objects. It only supposed to call these on the direct
-     * "children" of this object, not on the children of the children.
-     */
-    protected abstract void setConfigurationOfChildren(Configuration cfg);
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/cache/TemplateConfigurerFactoryException.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/cache/TemplateConfigurerFactoryException.java b/src/main/java/freemarker/cache/TemplateConfigurerFactoryException.java
deleted file mode 100644
index 2ddfe68..0000000
--- a/src/main/java/freemarker/cache/TemplateConfigurerFactoryException.java
+++ /dev/null
@@ -1,36 +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 freemarker.cache;
-
-/**
- * Non-I/O exception thrown by {@link TemplateConfigurerFactory}-s.  
- * 
- * @since 2.3.24
- */
-public class TemplateConfigurerFactoryException extends Exception {
-
-    public TemplateConfigurerFactoryException(String message) {
-        super(message);
-    }
-
-    public TemplateConfigurerFactoryException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/core/Configurable.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Configurable.java b/src/main/java/freemarker/core/Configurable.java
index c42fdcb..ba5d8c8 100644
--- a/src/main/java/freemarker/core/Configurable.java
+++ b/src/main/java/freemarker/core/Configurable.java
@@ -41,10 +41,10 @@ import java.util.Set;
 import java.util.TimeZone;
 
 import freemarker.cache.AndMatcher;
-import freemarker.cache.ConditionalTemplateConfigurerFactory;
+import freemarker.cache.ConditionalTemplateConfigurationFactory;
 import freemarker.cache.FileNameGlobMatcher;
-import freemarker.cache.FirstMatchTemplateConfigurerFactory;
-import freemarker.cache.MergingTemplateConfigurerFactory;
+import freemarker.cache.FirstMatchTemplateConfigurationFactory;
+import freemarker.cache.MergingTemplateConfigurationFactory;
 import freemarker.cache.NotMatcher;
 import freemarker.cache.OrMatcher;
 import freemarker.cache.PathGlobMatcher;
@@ -1807,8 +1807,8 @@ public class Configurable {
      *       <br>String value: {@code "default"} (case insensitive) for the default, or {@code "true"}, {@code "false"},
      *       {@code yes}, etc.
      *       
-     *   <li><p>{@code "template_configurers"}:
-     *       See: {@link Configuration#setTemplateConfigurers(freemarker.cache.TemplateConfigurerFactory)}.
+     *   <li><p>{@code "template_configurations"}:
+     *       See: {@link Configuration#setTemplateConfigurations(freemarker.cache.TemplateConfigurationFactory)}.
      *       <br>String value: Interpreted as an <a href="#fm_obe">object builder expression</a>,
      *       can be {@code null}.
      *       
@@ -1903,9 +1903,9 @@ public class Configurable {
      *   <li>
      *     <p>The following classes can be referred to with short class name instead of full qualified name:
      *     {@link DefaultObjectWrapper}, {@link BeansWrapper}, {@link SimpleObjectWrapper}, {@link Locale},
-     *     {@link TemplateConfigurer}, {@link PathGlobMatcher}, {@link FileNameGlobMatcher}, {@link PathRegexMatcher},
-     *     {@link AndMatcher}, {@link OrMatcher}, {@link NotMatcher}, {@link ConditionalTemplateConfigurerFactory},
-     *     {@link MergingTemplateConfigurerFactory}, {@link FirstMatchTemplateConfigurerFactory},
+     *     {@link TemplateConfiguration}, {@link PathGlobMatcher}, {@link FileNameGlobMatcher}, {@link PathRegexMatcher},
+     *     {@link AndMatcher}, {@link OrMatcher}, {@link NotMatcher}, {@link ConditionalTemplateConfigurationFactory},
+     *     {@link MergingTemplateConfigurationFactory}, {@link FirstMatchTemplateConfigurationFactory},
      *     {@link HTMLOutputFormat}, {@link XMLOutputFormat}, {@link RTFOutputFormat}, {@link PlainTextOutputFormat},
      *     {@link UndefinedOutputFormat}.
      *   </li>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/core/CustomAttribute.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/CustomAttribute.java b/src/main/java/freemarker/core/CustomAttribute.java
index 40ae92d..fe55eb9 100644
--- a/src/main/java/freemarker/core/CustomAttribute.java
+++ b/src/main/java/freemarker/core/CustomAttribute.java
@@ -131,15 +131,15 @@ public class CustomAttribute {
     }
     
     /**
-     * Same as {@link #get(Template)}, but applies to a {@link TemplateConfigurer}.  
+     * Same as {@link #get(Template)}, but applies to a {@link TemplateConfiguration}.  
      * 
      * @since 2.3.24
      */
-    public Object get(TemplateConfigurer templateConfigurer) {
+    public Object get(TemplateConfiguration templateConfiguration) {
         if (scope != SCOPE_TEMPLATE) {
             throw new UnsupportedOperationException("This is not a template-scope attribute");
         }
-        return templateConfigurer.getCustomAttribute(key, this);
+        return templateConfiguration.getCustomAttribute(key, this);
     }
     
     /**
@@ -209,15 +209,15 @@ public class CustomAttribute {
     }
 
     /**
-     * Same as {@link #set(Object, Template)}, but applicable to a {@link TemplateConfigurer}. 
+     * Same as {@link #set(Object, Template)}, but applicable to a {@link TemplateConfiguration}. 
      * 
      * @since 2.3.24
      */
-    public final void set(Object value, TemplateConfigurer templateConfigurer) {
+    public final void set(Object value, TemplateConfiguration templateConfiguration) {
         if (scope != SCOPE_TEMPLATE) {
             throw new UnsupportedOperationException("This is not a template-scope attribute");
         }
-        templateConfigurer.setCustomAttribute(key, value);
+        templateConfiguration.setCustomAttribute(key, value);
     }
     
     /**

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/core/TemplateConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateConfiguration.java b/src/main/java/freemarker/core/TemplateConfiguration.java
new file mode 100644
index 0000000..310028f
--- /dev/null
+++ b/src/main/java/freemarker/core/TemplateConfiguration.java
@@ -0,0 +1,578 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.core;
+
+import java.io.Reader;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import freemarker.cache.TemplateCache;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.Version;
+import freemarker.template._TemplateAPI;
+import freemarker.template.utility.NullArgumentException;
+
+/**
+ * Used for customizing the configuration settings for individual {@link Template}-s (or rather groups of templates),
+ * relatively to the common setting values coming from the {@link Configuration}. This was designed with the standard
+ * template loading mechanism of FreeMarker in mind ({@link Configuration#getTemplate(String)} and {@link TemplateCache}
+ * ), though can also be reused for custom template loading and caching solutions.
+ * 
+ * <p>
+ * Note on the {@code locale} setting: When used with the standard template loading/caching mechanism (
+ * {@link Configuration#getTemplate(String)} and its overloads), localized lookup happens before the {@code locale}
+ * specified here could have effect. The {@code locale} will be only set in the template that the localized looks has
+ * already found.
+ * 
+ * <p>
+ * Note on encoding setting {@code encoding}: See {@link #setEncoding(String)}.
+ * 
+ * <p>
+ * Note that the result value of the reader methods (getter and "is" methods) is usually not useful unless the value of
+ * that setting was already set on this object. Otherwise you will get the value from the parent {@link Configuration},
+ * which is {@link Configuration#getDefaultConfiguration()} before this object is associated to a {@link Configuration}.
+ * 
+ * <p>
+ * If you are using this class for your own template loading and caching solution, rather than with the standard one,
+ * you should be aware of a few more details:
+ * 
+ * <ul>
+ * <li>This class implements both {@link Configurable} and {@link ParserConfiguration}. This means that it can influence
+ * both the template parsing phase and the runtime settings. For both aspects (i.e., {@link ParserConfiguration} and
+ * {@link Configurable}) to take effect, you have first pass this object to the {@link Template} constructor
+ * (this is where the {@link ParserConfiguration} interface is used), and then you have to call {@link #apply(Template)}
+ * on the resulting {@link Template} object (this is where the {@link Configurable} aspect is used).
+ * 
+ * <li>{@link #apply(Template)} only change the settings that weren't yet set on the {@link Template} (but are inherited
+ * from the {@link Configuration}). This is primarily because if the template configures itself via the {@code #ftl}
+ * header, those values should have precedence. A consequence of this is that if you want to configure the same
+ * {@link Template} with multiple {@link TemplateConfiguration}-s, you either should merge them to a single one before
+ * that (with {@link #merge(TemplateConfiguration)}), or you have to apply them in reverse order of their intended
+ * precedence.
+ * </ul>
+ * 
+ * @see Template#Template(String, String, Reader, Configuration, ParserConfiguration, String)
+ * 
+ * @since 2.3.24
+ */
+public final class TemplateConfiguration extends Configurable implements ParserConfiguration {
+
+    private boolean parentConfigurationSet;
+    private Integer tagSyntax;
+    private Integer namingConvention;
+    private Boolean whitespaceStripping;
+    private Boolean strictSyntaxMode;
+    private Integer autoEscapingPolicy;
+    private Boolean recognizeStandardFileExtensions;
+    private OutputFormat outputFormat;
+    private String encoding;
+
+    /**
+     * Creates a new instance. The parent will be {@link Configuration#getDefaultConfiguration()} initially, but it will
+     * be changed to the real parent {@link Configuration} when this object is added to the {@link Configuration}. (It's
+     * not allowed to add the same instance to multiple {@link Configuration}-s).
+     */
+    public TemplateConfiguration() {
+        super(Configuration.getDefaultConfiguration());
+    }
+
+    /**
+     * Same as {@link #setParentConfiguration(Configuration)}.
+     */
+    @Override
+    void setParent(Configurable cfg) {
+        NullArgumentException.check("cfg", cfg);
+        if (!(cfg instanceof Configuration)) {
+            throw new IllegalArgumentException("The parent of a TemplateConfiguration can only be a Configuration");
+        }
+        
+        if (parentConfigurationSet) {
+            if (getParent() != cfg) {
+                throw new IllegalStateException(
+                        "This TemplateConfiguration is already associated with a different Configuration instance.");
+            }
+            return;
+        }
+        
+        if (((Configuration) cfg).getIncompatibleImprovements().intValue() < _TemplateAPI.VERSION_INT_2_3_22
+                && hasAnyConfigurableSet()) {
+            throw new IllegalStateException(
+                    "This TemplateConfiguration can't be associated to a Configuration that has incompatibleImprovements "
+                    + "less than 2.3.22, because it changes non-parser settings.");
+        }
+        
+        super.setParent(cfg);
+        parentConfigurationSet = true;
+    }
+
+    /**
+     * Associates this instance with a {@link Configuration}; usually you don't call this, as it's called internally
+     * when this instance is added to a {@link Configuration}. This method can be called only once (except with the same
+     * {@link Configuration} parameter again, as that changes nothing anyway).
+     * 
+     * @throws IllegalArgumentException
+     *             if the argument is {@code null} or not a {@link Configuration}
+     * @throws IllegalStateException
+     *             if this object is already associated to a different {@link Configuration} object,
+     *             or if the {@code Configuration} has {@code #getIncompatibleImprovements()} less than 2.3.22 and
+     *             this object tries to change any non-parser settings  
+     */
+    public void setParentConfiguration(Configuration cfg) {
+        setParent(cfg);
+    }
+
+    /**
+     * Returns the parent {@link Configuration}, or {@code null} if none was associated yet.
+     */
+    public Configuration getParentConfiguration() {
+        return parentConfigurationSet ? (Configuration) getParent() : null;
+    }
+    
+    /**
+     * Set all settings in this {@link TemplateConfiguration} that were set in the parameter
+     * {@link TemplateConfiguration}, possibly overwriting the earlier value in this object. (A setting is said to be
+     * set in a {@link TemplateConfiguration} if it was explicitly set via a setter method, as opposed to be inherited.)
+     */
+    public void merge(TemplateConfiguration tc) {
+        if (tc.isAPIBuiltinEnabledSet()) {
+            setAPIBuiltinEnabled(tc.isAPIBuiltinEnabled());
+        }
+        if (tc.isArithmeticEngineSet()) {
+            setArithmeticEngine(tc.getArithmeticEngine());
+        }
+        if (tc.isAutoEscapingPolicySet()) {
+            setAutoEscapingPolicy(tc.getAutoEscapingPolicy());
+        }
+        if (tc.isAutoFlushSet()) {
+            setAutoFlush(tc.getAutoFlush());
+        }
+        if (tc.isBooleanFormatSet()) {
+            setBooleanFormat(tc.getBooleanFormat());
+        }
+        if (tc.isClassicCompatibleSet()) {
+            setClassicCompatibleAsInt(tc.getClassicCompatibleAsInt());
+        }
+        if (tc.isCustomDateFormatsSet()) {
+            setCustomDateFormats(mergeMaps(getCustomDateFormats(), tc.getCustomDateFormats()));
+        }
+        if (tc.isCustomNumberFormatsSet()) {
+            setCustomNumberFormats(mergeMaps(getCustomNumberFormats(), tc.getCustomNumberFormats()));
+        }
+        if (tc.isDateFormatSet()) {
+            setDateFormat(tc.getDateFormat());
+        }
+        if (tc.isDateTimeFormatSet()) {
+            setDateTimeFormat(tc.getDateTimeFormat());
+        }
+        if (tc.isEncodingSet()) {
+            setEncoding(tc.getEncoding());
+        }
+        if (tc.isLocaleSet()) {
+            setLocale(tc.getLocale());
+        }
+        if (tc.isLogTemplateExceptionsSet()) {
+            setLogTemplateExceptions(tc.getLogTemplateExceptions());
+        }
+        if (tc.isNamingConventionSet()) {
+            setNamingConvention(tc.getNamingConvention());
+        }
+        if (tc.isNewBuiltinClassResolverSet()) {
+            setNewBuiltinClassResolver(tc.getNewBuiltinClassResolver());
+        }
+        if (tc.isNumberFormatSet()) {
+            setNumberFormat(tc.getNumberFormat());
+        }
+        if (tc.isObjectWrapperSet()) {
+            setObjectWrapper(tc.getObjectWrapper());
+        }
+        if (tc.isOutputEncodingSet()) {
+            setOutputEncoding(tc.getOutputEncoding());
+        }
+        if (tc.isOutputFormatSet()) {
+            setOutputFormat(tc.getOutputFormat());
+        }
+        if (tc.isRecognizeStandardFileExtensionsSet()) {
+            setRecognizeStandardFileExtensions(tc.getRecognizeStandardFileExtensions());
+        }
+        if (tc.isShowErrorTipsSet()) {
+            setShowErrorTips(tc.getShowErrorTips());
+        }
+        if (tc.isSQLDateAndTimeTimeZoneSet()) {
+            setSQLDateAndTimeTimeZone(tc.getSQLDateAndTimeTimeZone());
+        }
+        if (tc.isStrictSyntaxModeSet()) {
+            setStrictSyntaxMode(tc.getStrictSyntaxMode());
+        }
+        if (tc.isTagSyntaxSet()) {
+            setTagSyntax(tc.getTagSyntax());
+        }
+        if (tc.isTemplateExceptionHandlerSet()) {
+            setTemplateExceptionHandler(tc.getTemplateExceptionHandler());
+        }
+        if (tc.isTimeFormatSet()) {
+            setTimeFormat(tc.getTimeFormat());
+        }
+        if (tc.isTimeZoneSet()) {
+            setTimeZone(tc.getTimeZone());
+        }
+        if (tc.isURLEscapingCharsetSet()) {
+            setURLEscapingCharset(tc.getURLEscapingCharset());
+        }
+        if (tc.isWhitespaceStrippingSet()) {
+            setWhitespaceStripping(tc.getWhitespaceStripping());
+        }
+        
+        tc.copyDirectCustomAttributes(this, true);
+    }
+
+    /**
+     * Sets the settings of the {@link Template} which are not yet set in the {@link Template} and are set in this
+     * {@link TemplateConfiguration}, leaves the other settings as is. A setting is said to be set in a
+     * {@link TemplateConfiguration} or {@link Template} if it was explicitly set via a setter method on that object, as
+     * opposed to be inherited from the {@link Configuration}.
+     * 
+     * <p>
+     * Note that the {@code encoding} setting of the {@link Template} counts as unset if it's {@code null},
+     * even if {@code null} was set via {@link Template#setEncoding(String)}.
+     *
+     * @throws IllegalStateException
+     *             If the parent configuration wasn't yet set.
+     */
+    public void apply(Template template) {
+        checkParentConfigurationSet();
+        Configuration cfg = getParentConfiguration();
+        if (template.getConfiguration() != cfg) {
+            // This is actually not a problem right now, but for future BC we enforce this.
+            throw new IllegalArgumentException(
+                    "The argument Template doesn't belong to the same Configuration as the TemplateConfiguration");
+        }
+
+        if (isAPIBuiltinEnabledSet() && !template.isAPIBuiltinEnabledSet()) {
+            template.setAPIBuiltinEnabled(isAPIBuiltinEnabled());
+        }
+        if (isArithmeticEngineSet() && !template.isArithmeticEngineSet()) {
+            template.setArithmeticEngine(getArithmeticEngine());
+        }
+        if (isAutoFlushSet() && !template.isAutoFlushSet()) {
+            template.setAutoFlush(getAutoFlush());
+        }
+        if (isBooleanFormatSet() && !template.isBooleanFormatSet()) {
+            template.setBooleanFormat(getBooleanFormat());
+        }
+        if (isClassicCompatibleSet() && !template.isClassicCompatibleSet()) {
+            template.setClassicCompatibleAsInt(getClassicCompatibleAsInt());
+        }
+        if (isCustomDateFormatsSet() && !template.isCustomDateFormatsSet()) {
+            template.setCustomDateFormats(getCustomDateFormats());
+        }
+        if (isCustomNumberFormatsSet() && !template.isCustomNumberFormatsSet()) {
+            template.setCustomNumberFormats(getCustomNumberFormats());
+        }
+        if (isDateFormatSet() && !template.isDateFormatSet()) {
+            template.setDateFormat(getDateFormat());
+        }
+        if (isDateTimeFormatSet() && !template.isDateTimeFormatSet()) {
+            template.setDateTimeFormat(getDateTimeFormat());
+        }
+        if (isEncodingSet() && template.getEncoding() == null) {
+            template.setEncoding(getEncoding());
+        }
+        if (isLocaleSet() && !template.isLocaleSet()) {
+            template.setLocale(getLocale());
+        }
+        if (isLogTemplateExceptionsSet() && !template.isLogTemplateExceptionsSet()) {
+            template.setLogTemplateExceptions(getLogTemplateExceptions());
+        }
+        if (isNewBuiltinClassResolverSet() && !template.isNewBuiltinClassResolverSet()) {
+            template.setNewBuiltinClassResolver(getNewBuiltinClassResolver());
+        }
+        if (isNumberFormatSet() && !template.isNumberFormatSet()) {
+            template.setNumberFormat(getNumberFormat());
+        }
+        if (isObjectWrapperSet() && !template.isObjectWrapperSet()) {
+            template.setObjectWrapper(getObjectWrapper());
+        }
+        if (isOutputEncodingSet() && !template.isOutputEncodingSet()) {
+            template.setOutputEncoding(getOutputEncoding());
+        }
+        if (isShowErrorTipsSet() && !template.isShowErrorTipsSet()) {
+            template.setShowErrorTips(getShowErrorTips());
+        }
+        if (isSQLDateAndTimeTimeZoneSet() && !template.isSQLDateAndTimeTimeZoneSet()) {
+            template.setSQLDateAndTimeTimeZone(getSQLDateAndTimeTimeZone());
+        }
+        if (isTemplateExceptionHandlerSet() && !template.isTemplateExceptionHandlerSet()) {
+            template.setTemplateExceptionHandler(getTemplateExceptionHandler());
+        }
+        if (isTimeFormatSet() && !template.isTimeFormatSet()) {
+            template.setTimeFormat(getTimeFormat());
+        }
+        if (isTimeZoneSet() && !template.isTimeZoneSet()) {
+            template.setTimeZone(getTimeZone());
+        }
+        if (isURLEscapingCharsetSet() && !template.isURLEscapingCharsetSet()) {
+            template.setURLEscapingCharset(getURLEscapingCharset());
+        }
+        
+        copyDirectCustomAttributes(template, false);
+    }
+
+    /**
+     * See {@link Configuration#setTagSyntax(int)}.
+     */
+    public void setTagSyntax(int tagSyntax) {
+        _TemplateAPI.valideTagSyntaxValue(tagSyntax);
+        this.tagSyntax = Integer.valueOf(tagSyntax);
+    }
+
+    /**
+     * The getter pair of {@link #setTagSyntax(int)}.
+     */
+    public int getTagSyntax() {
+        return tagSyntax != null ? tagSyntax.intValue() : getParentConfiguration().getTagSyntax();
+    }
+
+    /**
+     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
+     */
+    public boolean isTagSyntaxSet() {
+        return tagSyntax != null;
+    }
+
+    /**
+     * See {@link Configuration#setNamingConvention(int)}.
+     */
+    public void setNamingConvention(int namingConvention) {
+        _TemplateAPI.validateNamingConventionValue(namingConvention);
+        this.namingConvention = Integer.valueOf(namingConvention);
+    }
+
+    /**
+     * The getter pair of {@link #setNamingConvention(int)}.
+     */
+    public int getNamingConvention() {
+        return namingConvention != null ? namingConvention.intValue() : getParentConfiguration().getNamingConvention();
+    }
+
+    /**
+     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
+     */
+    public boolean isNamingConventionSet() {
+        return namingConvention != null;
+    }
+
+    /**
+     * See {@link Configuration#setWhitespaceStripping(boolean)}.
+     */
+    public void setWhitespaceStripping(boolean whitespaceStripping) {
+        this.whitespaceStripping = Boolean.valueOf(whitespaceStripping);
+    }
+
+    /**
+     * The getter pair of {@link #getWhitespaceStripping()}.
+     */
+    public boolean getWhitespaceStripping() {
+        return whitespaceStripping != null ? whitespaceStripping.booleanValue()
+                : getParentConfiguration().getWhitespaceStripping();
+    }
+
+    /**
+     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
+     */
+    public boolean isWhitespaceStrippingSet() {
+        return whitespaceStripping != null;
+    }
+
+    /**
+     * Sets the output format of the template; see {@link Configuration#setAutoEscapingPolicy(int)} for more.
+     */
+    public void setAutoEscapingPolicy(int autoEscapingPolicy) {
+        _TemplateAPI.validateAutoEscapingPolicyValue(autoEscapingPolicy);
+        this.autoEscapingPolicy = Integer.valueOf(autoEscapingPolicy);
+    }
+
+    /**
+     * The getter pair of {@link #setAutoEscapingPolicy(int)}.
+     */
+    public int getAutoEscapingPolicy() {
+        return autoEscapingPolicy != null ? autoEscapingPolicy.intValue()
+                : getParentConfiguration().getAutoEscapingPolicy();
+    }
+
+    /**
+     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
+     */
+    public boolean isAutoEscapingPolicySet() {
+        return autoEscapingPolicy != null;
+    }
+
+    /**
+     * Sets the output format of the template; see {@link Configuration#setOutputFormat(OutputFormat)} for more.
+     */
+    public void setOutputFormat(OutputFormat outputFormat) {
+        NullArgumentException.check("outputFormat", outputFormat);
+        this.outputFormat = outputFormat;
+    }
+
+    /**
+     * The getter pair of {@link #setOutputFormat(OutputFormat)}.
+     */
+    public OutputFormat getOutputFormat() {
+        return outputFormat != null ? outputFormat : getParentConfiguration().getOutputFormat();
+    }
+
+    /**
+     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
+     */
+    public boolean isOutputFormatSet() {
+        return outputFormat != null;
+    }
+    
+    /**
+     * See {@link Configuration#setRecognizeStandardFileExtensions(boolean)}. 
+     */
+    public void setRecognizeStandardFileExtensions(boolean recognizeStandardFileExtensions) {
+        this.recognizeStandardFileExtensions = Boolean.valueOf(recognizeStandardFileExtensions);
+    }
+
+    /**
+     * Getter pair of {@link #setRecognizeStandardFileExtensions(boolean)}.
+     */
+    public boolean getRecognizeStandardFileExtensions() {
+        return recognizeStandardFileExtensions != null ? recognizeStandardFileExtensions.booleanValue()
+                : getParentConfiguration().getRecognizeStandardFileExtensions();
+    }
+    
+    /**
+     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
+     */
+    public boolean isRecognizeStandardFileExtensionsSet() {
+        return recognizeStandardFileExtensions != null;
+    }
+    
+    /**
+     * See {@link Configuration#setStrictSyntaxMode(boolean)}.
+     */
+    public void setStrictSyntaxMode(boolean strictSyntaxMode) {
+        this.strictSyntaxMode = Boolean.valueOf(strictSyntaxMode);
+    }
+
+    /**
+     * The getter pair of {@link #setStrictSyntaxMode(boolean)}.
+     */
+    public boolean getStrictSyntaxMode() {
+        return strictSyntaxMode != null ? strictSyntaxMode.booleanValue()
+                : getParentConfiguration().getStrictSyntaxMode();
+    }
+    
+    /**
+     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
+     */
+    public boolean isStrictSyntaxModeSet() {
+        return strictSyntaxMode != null;
+    }
+
+    @Override
+    public void setStrictBeanModels(boolean strict) {
+        throw new UnsupportedOperationException(
+                "Setting strictBeanModels on " + TemplateConfiguration.class.getSimpleName() + " level isn't supported.");
+    }
+
+    public String getEncoding() {
+        return encoding != null ? encoding : getParentConfiguration().getDefaultEncoding();
+    }
+
+    /**
+     * When the standard template loading/caching mechanism is used, this forces the charset used for reading the
+     * template "file", overriding everything but the encoding coming from the {@code #ftl} header. This setting
+     * overrides the locale-specific encodings set via {@link Configuration#setEncoding(java.util.Locale, String)}. It
+     * also overrides the {@code encoding} parameter of {@link Configuration#getTemplate(String, String)} (and of its
+     * overloads) and the {@code encoding} parameter of the {@code #include} directive. This works like that because
+     * specifying the encoding where you are requesting the template is error prone and deprecated.
+     * 
+     * <p>
+     * If you are developing your own template loading/caching mechanism instead of the standard one, note that the
+     * above behavior is not guaranteed by this class alone; you have to ensure it. Also, read the note on
+     * {@code encoding} in the documentation of {@link #apply(Template)}.
+     */
+    public void setEncoding(String encoding) {
+        NullArgumentException.check("encoding", encoding);
+        this.encoding = encoding;
+    }
+
+    public boolean isEncodingSet() {
+        return encoding != null;
+    }
+    
+    /**
+     * Returns {@link Configuration#getIncompatibleImprovements()} from the parent {@link Configuration}. This mostly
+     * just exist to satisfy the {@link ParserConfiguration} interface.
+     * 
+     * @throws IllegalStateException
+     *             If the parent configuration wasn't yet set.
+     */
+    public Version getIncompatibleImprovements() {
+        checkParentConfigurationSet();
+        return getParentConfiguration().getIncompatibleImprovements();
+    }
+
+    private void checkParentConfigurationSet() {
+        if (!parentConfigurationSet) {
+            throw new IllegalStateException("The TemplateConfiguration wasn't associated with a Configuration yet.");
+        }
+    }
+
+    private boolean hasAnyConfigurableSet() {
+        return
+                isAPIBuiltinEnabledSet()
+                || isArithmeticEngineSet()
+                || isAutoFlushSet()
+                || isBooleanFormatSet()
+                || isClassicCompatibleSet()
+                || isCustomDateFormatsSet()
+                || isCustomNumberFormatsSet()
+                || isDateFormatSet()
+                || isDateTimeFormatSet()
+                || isLocaleSet()
+                || isLogTemplateExceptionsSet()
+                || isNewBuiltinClassResolverSet()
+                || isNumberFormatSet()
+                || isObjectWrapperSet()
+                || isOutputEncodingSet()
+                || isShowErrorTipsSet()
+                || isSQLDateAndTimeTimeZoneSet()
+                || isTemplateExceptionHandlerSet()
+                || isTimeFormatSet()
+                || isTimeZoneSet()
+                || isURLEscapingCharsetSet();
+    }
+    
+    private Map mergeMaps(Map m1, Map m2) {
+        if (m1 == null) return m2;
+        if (m2 == null) return m1;
+        if (m1.isEmpty()) return m2;
+        if (m2.isEmpty()) return m1;
+        
+        LinkedHashMap mergedM = new LinkedHashMap(m1);
+        mergedM.putAll(m2);
+        return mergedM;
+    }
+    
+}


[2/7] incubator-freemarker git commit: The new (in 2.3.24-pre01) TemplateConfigurer class was renamed to TemplateConfiguration, and the related configuration setting from template_configurers to template_configurations. Also, the TemplateConfigurer.confi

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/resources/freemarker/manual/TemplateConfigurerExamples1.properties
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/TemplateConfigurerExamples1.properties b/src/test/resources/freemarker/manual/TemplateConfigurerExamples1.properties
deleted file mode 100644
index 30edee6..0000000
--- a/src/test/resources/freemarker/manual/TemplateConfigurerExamples1.properties
+++ /dev/null
@@ -1,8 +0,0 @@
-templateConfigurers = \
-    ConditionalTemplateConfigurerFactory( \
-        FileExtensionMatcher("xml"), \
-        TemplateConfigurer( \
-            encoding = "utf-8", \
-            outputFormat = XMLOutputFormat() \
-        ) \
-    )
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/resources/freemarker/manual/TemplateConfigurerExamples2.properties
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/TemplateConfigurerExamples2.properties b/src/test/resources/freemarker/manual/TemplateConfigurerExamples2.properties
deleted file mode 100644
index 07bd91b..0000000
--- a/src/test/resources/freemarker/manual/TemplateConfigurerExamples2.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-templateConfigurers = \
-    ConditionalTemplateConfigurerFactory( \
-        PathGlobMatcher("mail/**"), \
-        FirstMatchTemplateConfigurerFactory( \
-            ConditionalTemplateConfigurerFactory( \
-                FileNameGlobMatcher("*.subject.*"), \
-                TemplateConfigurer(outputFormat = PlainTextOutputFormat()) \
-            ), \
-            ConditionalTemplateConfigurerFactory( \
-                FileNameGlobMatcher("*.body.*"), \
-                TemplateConfigurer(outputFormat = HTMLOutputFormat()) \
-            ), \
-            noMatchErrorDetails = 'Mail template names must contain ".subject." or ".body."!' \
-        ) \
-    )

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/resources/freemarker/manual/TemplateConfigurerExamples3.properties
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/TemplateConfigurerExamples3.properties b/src/test/resources/freemarker/manual/TemplateConfigurerExamples3.properties
deleted file mode 100644
index bbdd5e8..0000000
--- a/src/test/resources/freemarker/manual/TemplateConfigurerExamples3.properties
+++ /dev/null
@@ -1,30 +0,0 @@
-templateConfigurers = \
-    MergingTemplateConfigurerFactory( \
-        ConditionalTemplateConfigurerFactory( \
-            FileNameGlobMatcher("*.stats.*"), \
-            TemplateConfigurer( \
-                dateTimeFormat = "iso", \
-                dateFormat = "iso", \
-                timeFormat = "iso", \
-                timeZone = TimeZone("UTC") \
-            ) \
-        ), \
-        ConditionalTemplateConfigurerFactory( \
-            PathGlobMatcher("mail/**"), \
-            TemplateConfigurer(encoding = "utf-8") \
-        ), \
-        FirstMatchTemplateConfigurerFactory( \
-            ConditionalTemplateConfigurerFactory( \
-                FileExtensionMatcher("xml"), \
-                TemplateConfigurer(outputFormat = XMLOutputFormat()) \
-            ), \
-            ConditionalTemplateConfigurerFactory( \
-                OrMatcher( \
-                    FileExtensionMatcher("html"), \
-                    FileExtensionMatcher("htm") \
-                ), \
-                TemplateConfigurer(outputFormat = HTMLOutputFormat()) \
-            ), \
-            allowNoMatch = true \
-        ) \
-    )


[3/7] incubator-freemarker git commit: The new (in 2.3.24-pre01) TemplateConfigurer class was renamed to TemplateConfiguration, and the related configuration setting from template_configurers to template_configurations. Also, the TemplateConfigurer.confi

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/core/TemplateConfigurerTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/TemplateConfigurerTest.java b/src/test/java/freemarker/core/TemplateConfigurerTest.java
deleted file mode 100644
index 382bdf0..0000000
--- a/src/test/java/freemarker/core/TemplateConfigurerTest.java
+++ /dev/null
@@ -1,846 +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 freemarker.core;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.TimeZone;
-
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableMap;
-
-import freemarker.template.Configuration;
-import freemarker.template.SimpleObjectWrapper;
-import freemarker.template.Template;
-import freemarker.template.TemplateException;
-import freemarker.template.TemplateExceptionHandler;
-import freemarker.template.Version;
-import freemarker.template.utility.NullArgumentException;
-
-@SuppressWarnings("boxing")
-public class TemplateConfigurerTest {
-
-    private final class DummyArithmeticEngine extends ArithmeticEngine {
-
-        @Override
-        public int compareNumbers(Number first, Number second) throws TemplateException {
-            return 0;
-        }
-
-        @Override
-        public Number add(Number first, Number second) throws TemplateException {
-            return 22;
-        }
-
-        @Override
-        public Number subtract(Number first, Number second) throws TemplateException {
-            return null;
-        }
-
-        @Override
-        public Number multiply(Number first, Number second) throws TemplateException {
-            return 33;
-        }
-
-        @Override
-        public Number divide(Number first, Number second) throws TemplateException {
-            return null;
-        }
-
-        @Override
-        public Number modulus(Number first, Number second) throws TemplateException {
-            return null;
-        }
-
-        @Override
-        public Number toNumber(String s) {
-            return 11;
-        }
-    }
-
-    private static final Version ICI = Configuration.VERSION_2_3_22;
-
-    private static final Configuration DEFAULT_CFG = new Configuration(ICI);
-
-    private static final TimeZone NON_DEFAULT_TZ;
-    static {
-        TimeZone defaultTZ = DEFAULT_CFG.getTimeZone();
-        TimeZone tz = TimeZone.getTimeZone("UTC");
-        if (tz.equals(defaultTZ)) {
-            tz = TimeZone.getTimeZone("GMT+01");
-            if (tz.equals(defaultTZ)) {
-                throw new AssertionError("Couldn't chose a non-default time zone");
-            }
-        }
-        NON_DEFAULT_TZ = tz;
-    }
-
-    private static final Locale NON_DEFAULT_LOCALE;
-    static {
-        Locale defaultLocale = DEFAULT_CFG.getLocale();
-        Locale locale = Locale.GERMAN;
-        if (locale.equals(defaultLocale)) {
-            locale = Locale.US;
-            if (locale.equals(defaultLocale)) {
-                throw new AssertionError("Couldn't chose a non-default locale");
-            }
-        }
-        NON_DEFAULT_LOCALE = locale;
-    }
-
-    private static final String NON_DEFAULT_ENCODING;
-
-    static {
-        String defaultEncoding = DEFAULT_CFG.getDefaultEncoding();
-        String encoding = "UTF-16";
-        if (encoding.equals(defaultEncoding)) {
-            encoding = "UTF-8";
-            if (encoding.equals(defaultEncoding)) {
-                throw new AssertionError("Couldn't chose a non-default locale");
-            }
-        }
-        NON_DEFAULT_ENCODING = encoding;
-    }
-    
-    private static final Map<String, Object> SETTING_ASSIGNMENTS;
-
-    static {
-        SETTING_ASSIGNMENTS = new HashMap<String, Object>();
-
-        // "Configurable" settings:
-        SETTING_ASSIGNMENTS.put("APIBuiltinEnabled", true);
-        SETTING_ASSIGNMENTS.put("SQLDateAndTimeTimeZone", NON_DEFAULT_TZ);
-        SETTING_ASSIGNMENTS.put("URLEscapingCharset", "utf-16");
-        SETTING_ASSIGNMENTS.put("autoFlush", false);
-        SETTING_ASSIGNMENTS.put("booleanFormat", "J,N");
-        SETTING_ASSIGNMENTS.put("classicCompatibleAsInt", 2);
-        SETTING_ASSIGNMENTS.put("dateFormat", "yyyy-#DDD");
-        SETTING_ASSIGNMENTS.put("dateTimeFormat", "yyyy-#DDD-@HH:mm");
-        SETTING_ASSIGNMENTS.put("locale", NON_DEFAULT_LOCALE);
-        SETTING_ASSIGNMENTS.put("logTemplateExceptions", false);
-        SETTING_ASSIGNMENTS.put("newBuiltinClassResolver", TemplateClassResolver.ALLOWS_NOTHING_RESOLVER);
-        SETTING_ASSIGNMENTS.put("numberFormat", "0.0000");
-        SETTING_ASSIGNMENTS.put("objectWrapper", new SimpleObjectWrapper(ICI));
-        SETTING_ASSIGNMENTS.put("outputEncoding", "utf-16");
-        SETTING_ASSIGNMENTS.put("showErrorTips", false);
-        SETTING_ASSIGNMENTS.put("templateExceptionHandler", TemplateExceptionHandler.IGNORE_HANDLER);
-        SETTING_ASSIGNMENTS.put("timeFormat", "@HH:mm");
-        SETTING_ASSIGNMENTS.put("timeZone", NON_DEFAULT_TZ);
-        SETTING_ASSIGNMENTS.put("arithmeticEngine", ArithmeticEngine.CONSERVATIVE_ENGINE);
-        SETTING_ASSIGNMENTS.put("customNumberFormats",
-                ImmutableMap.of("dummy", HexTemplateNumberFormatFactory.INSTANCE));
-        SETTING_ASSIGNMENTS.put("customDateFormats",
-                ImmutableMap.of("dummy", EpochMillisTemplateDateFormatFactory.INSTANCE));
-
-        // Parser-only settings:
-        SETTING_ASSIGNMENTS.put("tagSyntax", Configuration.SQUARE_BRACKET_TAG_SYNTAX);
-        SETTING_ASSIGNMENTS.put("namingConvention", Configuration.LEGACY_NAMING_CONVENTION);
-        SETTING_ASSIGNMENTS.put("whitespaceStripping", false);
-        SETTING_ASSIGNMENTS.put("strictSyntaxMode", false);
-        SETTING_ASSIGNMENTS.put("autoEscapingPolicy", Configuration.DISABLE_AUTO_ESCAPING_POLICY);
-        SETTING_ASSIGNMENTS.put("outputFormat", HTMLOutputFormat.INSTANCE);
-        SETTING_ASSIGNMENTS.put("recognizeStandardFileExtensions", true);
-        
-        // Special settings:
-        SETTING_ASSIGNMENTS.put("encoding", NON_DEFAULT_ENCODING);
-    }
-    
-    public static String getIsSetMethodName(String readMethodName) {
-        String isSetMethodName = (readMethodName.startsWith("get") ? "is" + readMethodName.substring(3)
-                : readMethodName)
-                + "Set";
-        if (isSetMethodName.equals("isClassicCompatibleAsIntSet")) {
-            isSetMethodName = "isClassicCompatibleSet";
-        }
-        return isSetMethodName;
-    }
-
-    public static List<PropertyDescriptor> getTemplateConfigurerSettingPropDescs(
-            boolean includeCompilerSettings, boolean includeSpecialSettings)
-            throws IntrospectionException {
-        List<PropertyDescriptor> settingPropDescs = new ArrayList<PropertyDescriptor>();
-
-        BeanInfo beanInfo = Introspector.getBeanInfo(TemplateConfigurer.class);
-        for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
-            String name = pd.getName();
-            if (pd.getWriteMethod() != null && !IGNORED_PROP_NAMES.contains(name)
-                    && (includeCompilerSettings
-                            || (CONFIGURABLE_PROP_NAMES.contains(name) || !PARSER_PROP_NAMES.contains(name)))
-                    && (includeSpecialSettings
-                            || !SPECIAL_PROP_NAMES.contains(name))) {
-                if (pd.getReadMethod() == null) {
-                    throw new AssertionError("Property has no read method: " + pd);
-                }
-                settingPropDescs.add(pd);
-            }
-        }
-
-        Collections.sort(settingPropDescs, new Comparator<PropertyDescriptor>() {
-
-            public int compare(PropertyDescriptor o1, PropertyDescriptor o2) {
-                return o1.getName().compareToIgnoreCase(o2.getName());
-            }
-        });
-
-        return settingPropDescs;
-    }
-
-    private static final Set<String> IGNORED_PROP_NAMES;
-
-    static {
-        IGNORED_PROP_NAMES = new HashSet();
-        IGNORED_PROP_NAMES.add("class");
-        IGNORED_PROP_NAMES.add("strictBeanModels");
-        IGNORED_PROP_NAMES.add("parentConfiguration");
-        IGNORED_PROP_NAMES.add("settings");
-        IGNORED_PROP_NAMES.add("classicCompatible");
-    }
-
-    private static final Set<String> CONFIGURABLE_PROP_NAMES;
-    static {
-        CONFIGURABLE_PROP_NAMES = new HashSet<String>();
-        try {
-            for (PropertyDescriptor propDesc : Introspector.getBeanInfo(Configurable.class).getPropertyDescriptors()) {
-                String propName = propDesc.getName();
-                if (!IGNORED_PROP_NAMES.contains(propName)) {
-                    CONFIGURABLE_PROP_NAMES.add(propName);
-                }
-            }
-        } catch (IntrospectionException e) {
-            throw new IllegalStateException("Failed to init static field", e);
-        }
-    }
-    
-    private static final Set<String> PARSER_PROP_NAMES;
-    static {
-        PARSER_PROP_NAMES = new HashSet<String>();
-        // It's an interface; can't use standard Inrospector
-        for (Method m : ParserConfiguration.class.getMethods()) {
-            String propertyName;
-            if (m.getName().startsWith("get")) {
-                propertyName = m.getName().substring(3);
-            } else if (m.getName().startsWith("is")) {
-                propertyName = m.getName().substring(2);
-            } else {
-                propertyName = null;
-            }
-            if (propertyName != null) {
-                if (!Character.isUpperCase(propertyName.charAt(1))) {
-                    propertyName = Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1);
-                }
-                PARSER_PROP_NAMES.add(propertyName);
-            }
-        }
-    }
-
-    private static final Set<String> SPECIAL_PROP_NAMES;
-    static {
-        SPECIAL_PROP_NAMES = new HashSet<String>();
-        SPECIAL_PROP_NAMES.add("encoding");
-    }
-    
-    private static final CustomAttribute CA1 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); 
-    private static final CustomAttribute CA2 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); 
-    private static final CustomAttribute CA3 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); 
-    private static final CustomAttribute CA4 = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); 
-
-    @Test
-    public void testMergeBasicFunctionality() throws Exception {
-        for (PropertyDescriptor propDesc1 : getTemplateConfigurerSettingPropDescs(true, true)) {
-            for (PropertyDescriptor propDesc2 : getTemplateConfigurerSettingPropDescs(true, true)) {
-                TemplateConfigurer tc1 = new TemplateConfigurer();
-                TemplateConfigurer tc2 = new TemplateConfigurer();
-
-                Object value1 = SETTING_ASSIGNMENTS.get(propDesc1.getName());
-                propDesc1.getWriteMethod().invoke(tc1, value1);
-                Object value2 = SETTING_ASSIGNMENTS.get(propDesc2.getName());
-                propDesc2.getWriteMethod().invoke(tc2, value2);
-
-                tc1.merge(tc2);
-                Object mValue1 = propDesc1.getReadMethod().invoke(tc1);
-                Object mValue2 = propDesc2.getReadMethod().invoke(tc1);
-
-                assertEquals("For " + propDesc1.getName(), value1, mValue1);
-                assertEquals("For " + propDesc2.getName(), value2, mValue2);
-            }
-        }
-    }
-    
-    @Test
-    public void testMergeMapSettings() throws Exception {
-        TemplateConfigurer tc1 = new TemplateConfigurer();
-        tc1.setCustomDateFormats(ImmutableMap.of(
-                "epoch", EpochMillisTemplateDateFormatFactory.INSTANCE,
-                "x", LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE));
-        tc1.setCustomNumberFormats(ImmutableMap.of(
-                "hex", HexTemplateNumberFormatFactory.INSTANCE,
-                "x", LocaleSensitiveTemplateNumberFormatFactory.INSTANCE));
-        
-        TemplateConfigurer tc2 = new TemplateConfigurer();
-        tc2.setCustomDateFormats(ImmutableMap.of(
-                "loc", LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE,
-                "x", EpochMillisDivTemplateDateFormatFactory.INSTANCE));
-        tc2.setCustomNumberFormats(ImmutableMap.of(
-                "loc", LocaleSensitiveTemplateNumberFormatFactory.INSTANCE,
-                "x", BaseNTemplateNumberFormatFactory.INSTANCE));
-        
-        tc1.merge(tc2);
-        
-        Map<String, ? extends TemplateDateFormatFactory> mergedCustomDateFormats = tc1.getCustomDateFormats();
-        assertEquals(EpochMillisTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("epoch"));
-        assertEquals(LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("loc"));
-        assertEquals(EpochMillisDivTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("x"));
-        
-        Map<String, ? extends TemplateNumberFormatFactory> mergedCustomNumberFormats = tc1.getCustomNumberFormats();
-        assertEquals(HexTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("hex"));
-        assertEquals(LocaleSensitiveTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("loc"));
-        assertEquals(BaseNTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("x"));
-        
-        // Empty map merging optimization:
-        tc1.merge(new TemplateConfigurer());
-        assertSame(mergedCustomDateFormats, tc1.getCustomDateFormats());
-        assertSame(mergedCustomNumberFormats, tc1.getCustomNumberFormats());
-        
-        // Empty map merging optimization:
-        TemplateConfigurer tc3 = new TemplateConfigurer();
-        tc3.merge(tc1);
-        assertSame(mergedCustomDateFormats, tc3.getCustomDateFormats());
-        assertSame(mergedCustomNumberFormats, tc3.getCustomNumberFormats());
-    }
-    
-    @Test
-    public void testMergePriority() throws Exception {
-        TemplateConfigurer tc1 = new TemplateConfigurer();
-        tc1.setDateFormat("1");
-        tc1.setTimeFormat("1");
-        tc1.setDateTimeFormat("1");
-
-        TemplateConfigurer tc2 = new TemplateConfigurer();
-        tc2.setDateFormat("2");
-        tc2.setTimeFormat("2");
-
-        TemplateConfigurer tc3 = new TemplateConfigurer();
-        tc3.setDateFormat("3");
-
-        tc1.merge(tc2);
-        tc1.merge(tc3);
-
-        assertEquals("3", tc1.getDateFormat());
-        assertEquals("2", tc1.getTimeFormat());
-        assertEquals("1", tc1.getDateTimeFormat());
-    }
-    
-    @Test
-    public void testMergeCustomAttributes() throws Exception {
-        TemplateConfigurer tc1 = new TemplateConfigurer();
-        tc1.setCustomAttribute("k1", "v1");
-        tc1.setCustomAttribute("k2", "v1");
-        tc1.setCustomAttribute("k3", "v1");
-        CA1.set("V1", tc1);
-        CA2.set("V1", tc1);
-        CA3.set("V1", tc1);
-
-        TemplateConfigurer tc2 = new TemplateConfigurer();
-        tc2.setCustomAttribute("k1", "v2");
-        tc2.setCustomAttribute("k2", "v2");
-        CA1.set("V2", tc2);
-        CA2.set("V2", tc2);
-
-        TemplateConfigurer tc3 = new TemplateConfigurer();
-        tc3.setCustomAttribute("k1", "v3");
-        CA1.set("V3", tc2);
-
-        tc1.merge(tc2);
-        tc1.merge(tc3);
-
-        assertEquals("v3", tc1.getCustomAttribute("k1"));
-        assertEquals("v2", tc1.getCustomAttribute("k2"));
-        assertEquals("v1", tc1.getCustomAttribute("k3"));
-        assertEquals("V3", CA1.get(tc1));
-        assertEquals("V2", CA2.get(tc1));
-        assertEquals("V1", CA3.get(tc1));
-    }
-    
-    @Test
-    public void testMergeNullCustomAttributes() throws Exception {
-        TemplateConfigurer tc1 = new TemplateConfigurer();
-        tc1.setCustomAttribute("k1", "v1");
-        tc1.setCustomAttribute("k2", "v1");
-        tc1.setCustomAttribute(null, "v1");
-        CA1.set("V1", tc1);
-        CA2.set("V1", tc1);
-        CA3.set(null, tc1);
-        
-        assertEquals("v1", tc1.getCustomAttribute("k1"));
-        assertEquals("v1", tc1.getCustomAttribute("k2"));
-        assertNull("v1", tc1.getCustomAttribute("k3"));
-        assertEquals("V1", CA1.get(tc1));
-        assertEquals("V1", CA2.get(tc1));
-        assertNull(CA3.get(tc1));
-
-        TemplateConfigurer tc2 = new TemplateConfigurer();
-        tc2.setCustomAttribute("k1", "v2");
-        tc2.setCustomAttribute("k2", null);
-        CA1.set("V2", tc2);
-        CA2.set(null, tc2);
-
-        TemplateConfigurer tc3 = new TemplateConfigurer();
-        tc3.setCustomAttribute("k1", null);
-        CA1.set(null, tc2);
-
-        tc1.merge(tc2);
-        tc1.merge(tc3);
-
-        assertNull(tc1.getCustomAttribute("k1"));
-        assertNull(tc1.getCustomAttribute("k2"));
-        assertNull(tc1.getCustomAttribute("k3"));
-        assertNull(CA1.get(tc1));
-        assertNull(CA2.get(tc1));
-        assertNull(CA3.get(tc1));
-        
-        TemplateConfigurer tc4 = new TemplateConfigurer();
-        tc4.setCustomAttribute("k1", "v4");
-        CA1.set("V4", tc4);
-        
-        tc1.merge(tc4);
-        
-        assertEquals("v4", tc1.getCustomAttribute("k1"));
-        assertNull(tc1.getCustomAttribute("k2"));
-        assertNull(tc1.getCustomAttribute("k3"));
-        assertEquals("V4", CA1.get(tc1));
-        assertNull(CA2.get(tc1));
-        assertNull(CA3.get(tc1));
-    }
-
-    @Test
-    public void testConfigureNonParserConfig() throws Exception {
-        for (PropertyDescriptor pd : getTemplateConfigurerSettingPropDescs(false, true)) {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-    
-            Object newValue = SETTING_ASSIGNMENTS.get(pd.getName());
-            pd.getWriteMethod().invoke(tc, newValue);
-            
-            Template t = new Template(null, "", DEFAULT_CFG);
-            Method tReaderMethod = t.getClass().getMethod(pd.getReadMethod().getName());
-            
-            assertNotEquals("For \"" + pd.getName() + "\"", newValue, tReaderMethod.invoke(t));
-            tc.configure(t);
-            assertEquals("For \"" + pd.getName() + "\"", newValue, tReaderMethod.invoke(t));
-        }
-    }
-    
-    @Test
-    public void testConfigureCustomAttributes() throws Exception {
-        Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
-        cfg.setCustomAttribute("k1", "c");
-        cfg.setCustomAttribute("k2", "c");
-        cfg.setCustomAttribute("k3", "c");
-
-        TemplateConfigurer tc = new TemplateConfigurer();
-        tc.setCustomAttribute("k2", "tc");
-        tc.setCustomAttribute("k3", null);
-        tc.setCustomAttribute("k4", "tc");
-        tc.setCustomAttribute("k5", "tc");
-        tc.setCustomAttribute("k6", "tc");
-        CA1.set("tc", tc);
-        CA2.set("tc", tc);
-        CA3.set("tc", tc);
-
-        Template t = new Template(null, "", cfg);
-        t.setCustomAttribute("k5", "t");
-        t.setCustomAttribute("k6", null);
-        t.setCustomAttribute("k7", "t");
-        CA2.set("t", t);
-        CA3.set(null, t);
-        CA4.set("t", t);
-        
-        tc.setParentConfiguration(cfg);
-        tc.configure(t);
-        
-        assertEquals("c", t.getCustomAttribute("k1"));
-        assertEquals("tc", t.getCustomAttribute("k2"));
-        assertNull(t.getCustomAttribute("k3"));
-        assertEquals("tc", t.getCustomAttribute("k4"));
-        assertEquals("t", t.getCustomAttribute("k5"));
-        assertNull(t.getCustomAttribute("k6"));
-        assertEquals("t", t.getCustomAttribute("k7"));
-        assertEquals("tc", CA1.get(t));
-        assertEquals("t", CA2.get(t));
-        assertNull(CA3.get(t));
-        assertEquals("t", CA4.get(t));
-    }
-    
-    @Test
-    public void testConfigureParser() throws Exception {
-        Set<String> testedProps = new HashSet<String>();
-        
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-            tc.setTagSyntax(Configuration.SQUARE_BRACKET_TAG_SYNTAX);
-            assertOutputWithoutAndWithTC(tc, "[#if true]y[/#if]", "[#if true]y[/#if]", "y");
-            testedProps.add(Configuration.TAG_SYNTAX_KEY_CAMEL_CASE);
-        }
-        
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-            tc.setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
-            assertOutputWithoutAndWithTC(tc, "<#if true>y<#elseif false>n</#if>", "y", null);
-            testedProps.add(Configuration.NAMING_CONVENTION_KEY_CAMEL_CASE);
-        }
-        
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-            tc.setWhitespaceStripping(false);
-            assertOutputWithoutAndWithTC(tc, "<#if true>\nx\n</#if>\n", "x\n", "\nx\n\n");
-            testedProps.add(Configuration.WHITESPACE_STRIPPING_KEY_CAMEL_CASE);
-        }
-
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-            tc.setArithmeticEngine(new DummyArithmeticEngine());
-            assertOutputWithoutAndWithTC(tc, "${1} ${1+1}", "1 2", "11 22");
-            testedProps.add(Configuration.ARITHMETIC_ENGINE_KEY_CAMEL_CASE);
-        }
-
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-            tc.setOutputFormat(XMLOutputFormat.INSTANCE);
-            assertOutputWithoutAndWithTC(tc, "${.outputFormat} ${\"a'b\"}",
-                    UndefinedOutputFormat.INSTANCE.getName() + " a'b",
-                    XMLOutputFormat.INSTANCE.getName() + " a&apos;b");
-            testedProps.add(Configuration.OUTPUT_FORMAT_KEY_CAMEL_CASE);
-        }
-
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-            tc.setOutputFormat(XMLOutputFormat.INSTANCE);
-            tc.setAutoEscapingPolicy(Configuration.DISABLE_AUTO_ESCAPING_POLICY);
-            assertOutputWithoutAndWithTC(tc, "${'a&b'}", "a&b", "a&b");
-            testedProps.add(Configuration.AUTO_ESCAPING_POLICY_KEY_CAMEL_CASE);
-        }
-        
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-            tc.setStrictSyntaxMode(false);
-            assertOutputWithoutAndWithTC(tc, "<if true>y</if>", "<if true>y</if>", "y");
-            testedProps.add("strictSyntaxMode");
-        }
-
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(new Configuration(new Version(2, 3, 0)));
-            assertOutputWithoutAndWithTC(tc, "<#foo>", null, "<#foo>");
-            testedProps.add(Configuration.INCOMPATIBLE_IMPROVEMENTS_KEY_CAMEL_CASE);
-        }
-
-
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(new Configuration(new Version(2, 3, 0)));
-            tc.setRecognizeStandardFileExtensions(true);
-            assertOutputWithoutAndWithTC(tc, "${.outputFormat}",
-                    UndefinedOutputFormat.INSTANCE.getName(), HTMLOutputFormat.INSTANCE.getName());
-            testedProps.add(Configuration.RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY_CAMEL_CASE);
-        }
-        
-        assertEquals("Check that you have tested all parser settings; ", PARSER_PROP_NAMES, testedProps);
-    }
-    
-    @Test
-    public void testConfigureParserTooLowIcI() throws Exception {
-        Configuration cfgWithTooLowIcI = new Configuration(Configuration.VERSION_2_3_21);
-        for (PropertyDescriptor propDesc : getTemplateConfigurerSettingPropDescs(true, false)) {
-            TemplateConfigurer tc = new TemplateConfigurer();
-
-            String propName = propDesc.getName();
-            Object value = SETTING_ASSIGNMENTS.get(propName);
-            propDesc.getWriteMethod().invoke(tc, value);
-            
-            boolean shouldFail;
-            if (CONFIGURABLE_PROP_NAMES.contains(propName)) {
-                shouldFail = true;
-            } else if (PARSER_PROP_NAMES.contains(propName)) {
-                shouldFail = false;
-            } else {
-                fail("Uncategorized property: " + propName);
-                return;
-            }
-            
-            try {
-                tc.setParentConfiguration(cfgWithTooLowIcI);
-                if (shouldFail) {
-                    fail("Should fail with property: " + propName);
-                }
-            } catch (IllegalStateException e) {
-                if (!shouldFail) {
-                    throw e;
-                }
-                assertThat(e.getMessage(), containsString("2.3.22"));
-            }
-        }
-    }
-    
-    @Test
-    public void testArithmeticEngine() throws TemplateException, IOException {
-        TemplateConfigurer tc = new TemplateConfigurer();
-        tc.setParentConfiguration(DEFAULT_CFG);
-        tc.setArithmeticEngine(new DummyArithmeticEngine());
-        assertOutputWithoutAndWithTC(tc,
-                "<#setting locale='en_US'>${1} ${1+1} ${1*3} <#assign x = 1>${x + x} ${x * 3}",
-                "1 2 3 2 3", "11 22 33 22 33");
-        
-        // Doesn't affect template.arithmeticEngine, only affects the parsing:
-        Template t = new Template(null, null, new StringReader(""), DEFAULT_CFG, tc, null);
-        assertEquals(DEFAULT_CFG.getArithmeticEngine(), t.getArithmeticEngine());
-    }
-
-    @Test
-    public void testStringInterpolate() throws TemplateException, IOException {
-        TemplateConfigurer tc = new TemplateConfigurer();
-        tc.setParentConfiguration(DEFAULT_CFG);
-        tc.setArithmeticEngine(new DummyArithmeticEngine());
-        assertOutputWithoutAndWithTC(tc,
-                "<#setting locale='en_US'>${'${1} ${1+1} ${1*3}'} <#assign x = 1>${'${x + x} ${x * 3}'}",
-                "1 2 3 2 3", "11 22 33 22 33");
-        
-        // Doesn't affect template.arithmeticEngine, only affects the parsing:
-        Template t = new Template(null, null, new StringReader(""), DEFAULT_CFG, tc, null);
-        assertEquals(DEFAULT_CFG.getArithmeticEngine(), t.getArithmeticEngine());
-    }
-    
-    @Test
-    public void testInterpret() throws TemplateException, IOException {
-        TemplateConfigurer tc = new TemplateConfigurer();
-        tc.setParentConfiguration(DEFAULT_CFG);
-        tc.setArithmeticEngine(new DummyArithmeticEngine());
-        assertOutputWithoutAndWithTC(tc,
-                "<#setting locale='en_US'><#assign src = r'${1} <#assign x = 1>${x + x}'><@src?interpret />",
-                "1 2", "11 22");
-        
-        tc.setWhitespaceStripping(false);
-        assertOutputWithoutAndWithTC(tc,
-                "<#if true>\nX</#if><#assign src = r'<#if true>\nY</#if>'><@src?interpret />",
-                "XY", "\nX\nY");
-    }
-
-    @Test
-    public void testEval() throws TemplateException, IOException {
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-            tc.setArithmeticEngine(new DummyArithmeticEngine());
-            assertOutputWithoutAndWithTC(tc,
-                    "<#assign x = 1>${r'1 + x'?eval?c}",
-                    "2", "22");
-            assertOutputWithoutAndWithTC(tc,
-                    "${r'1?c'?eval}",
-                    "1", "11");
-        }
-        
-        {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            tc.setParentConfiguration(DEFAULT_CFG);
-            String outputEncoding = "ISO-8859-2";
-            tc.setOutputEncoding(outputEncoding);
-
-            String legacyNCFtl = "${r'.output_encoding!\"null\"'?eval}";
-            String camelCaseNCFtl = "${r'.outputEncoding!\"null\"'?eval}";
-
-            // Default is re-auto-detecting in ?eval:
-            assertOutputWithoutAndWithTC(tc, legacyNCFtl, "null", outputEncoding);
-            assertOutputWithoutAndWithTC(tc, camelCaseNCFtl, "null", outputEncoding);
-            
-            // Force camelCase:
-            tc.setNamingConvention(Configuration.CAMEL_CASE_NAMING_CONVENTION);
-            assertOutputWithoutAndWithTC(tc, legacyNCFtl, "null", null);
-            assertOutputWithoutAndWithTC(tc, camelCaseNCFtl, "null", outputEncoding);
-            
-            // Force legacy:
-            tc.setNamingConvention(Configuration.LEGACY_NAMING_CONVENTION);
-            assertOutputWithoutAndWithTC(tc, legacyNCFtl, "null", outputEncoding);
-            assertOutputWithoutAndWithTC(tc, camelCaseNCFtl, "null", null);
-        }
-    }
-    
-    @Test
-    public void testSetParentConfiguration() throws IOException {
-        TemplateConfigurer tc = new TemplateConfigurer();
-        
-        Template t = new Template(null, "", DEFAULT_CFG);
-        try {
-            tc.configure(t);
-            fail();
-        } catch (IllegalStateException e) {
-            assertThat(e.getMessage(), containsString("Configuration"));
-        }
-        
-        tc.setParent(DEFAULT_CFG);
-        
-        try {
-            tc.setParentConfiguration(new Configuration());
-            fail();
-        } catch (IllegalStateException e) {
-            assertThat(e.getMessage(), containsString("Configuration"));
-        }
-
-        try {
-            // Same as setParentConfiguration
-            tc.setParent(new Configuration());
-            fail();
-        } catch (IllegalStateException e) {
-            assertThat(e.getMessage(), containsString("Configuration"));
-        }
-        
-        try {
-            tc.setParentConfiguration(null);
-            fail();
-        } catch (NullArgumentException e) {
-            // exected
-        }
-        
-        tc.setParent(DEFAULT_CFG);
-        
-        tc.configure(t);
-    }
-    
-    private void assertOutputWithoutAndWithTC(TemplateConfigurer tc, String ftl, String expectedDefaultOutput,
-            String expectedConfiguredOutput) throws TemplateException, IOException {
-        assertOutput(tc, ftl, expectedConfiguredOutput);
-        assertOutput(null, ftl, expectedDefaultOutput);
-    }
-
-    private void assertOutput(TemplateConfigurer tc, String ftl, String expectedConfiguredOutput)
-            throws TemplateException, IOException {
-        StringWriter sw = new StringWriter();
-        try {
-            Configuration cfg = tc != null ? tc.getParentConfiguration() : DEFAULT_CFG;
-            Template t = new Template("adhoc.ftlh", null, new StringReader(ftl), cfg, tc, null);
-            if (tc != null) {
-                tc.configure(t);
-            }
-            t.process(null, sw);
-            if (expectedConfiguredOutput == null) {
-                fail("Template should have fail.");
-            }
-        } catch (TemplateException e) {
-            if (expectedConfiguredOutput != null) {
-                throw e;
-            }
-        } catch (ParseException e) {
-            if (expectedConfiguredOutput != null) {
-                throw e;
-            }
-        }
-        if (expectedConfiguredOutput != null) {
-            assertEquals(expectedConfiguredOutput, sw.toString());
-        }
-    }
-
-    @Test
-    public void testIsSet() throws Exception {
-        for (PropertyDescriptor pd : getTemplateConfigurerSettingPropDescs(true, true)) {
-            TemplateConfigurer tc = new TemplateConfigurer();
-            checkAllIsSetFalseExcept(tc, null);
-            pd.getWriteMethod().invoke(tc, SETTING_ASSIGNMENTS.get(pd.getName()));
-            checkAllIsSetFalseExcept(tc, pd.getName());
-        }
-    }
-
-    private void checkAllIsSetFalseExcept(TemplateConfigurer tc, String setSetting)
-            throws SecurityException, IntrospectionException,
-            IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-        for (PropertyDescriptor pd : getTemplateConfigurerSettingPropDescs(true, true)) {
-            String isSetMethodName = getIsSetMethodName(pd.getReadMethod().getName());
-            Method isSetMethod;
-            try {
-                isSetMethod = TemplateConfigurer.class.getMethod(isSetMethodName);
-            } catch (NoSuchMethodException e) {
-                fail("Missing " + isSetMethodName + " method for \"" + pd.getName() + "\".");
-                return;
-            }
-            if (pd.getName().equals(setSetting)) {
-                assertTrue(isSetMethod + " should return true", (Boolean) (isSetMethod.invoke(tc)));
-            } else {
-                assertFalse(isSetMethod + " should return false", (Boolean) (isSetMethod.invoke(tc)));
-            }
-        }
-    }
-
-    /**
-     * Test case self-check.
-     */
-    @Test
-    public void checkTestAssignments() throws Exception {
-        for (PropertyDescriptor pd : getTemplateConfigurerSettingPropDescs(true, true)) {
-            String propName = pd.getName();
-            if (!SETTING_ASSIGNMENTS.containsKey(propName)) {
-                fail("Test case doesn't cover all settings in SETTING_ASSIGNMENTS. Missing: " + propName);
-            }
-            Method readMethod = pd.getReadMethod();
-            String cfgMethodName = readMethod.getName();
-            if (cfgMethodName.equals("getEncoding")) {
-                // Because Configuration has local-to-encoding map too, this has a different name there.
-                cfgMethodName = "getDefaultEncoding";
-            }
-            Method cfgMethod = DEFAULT_CFG.getClass().getMethod(cfgMethodName, readMethod.getParameterTypes());
-            Object defaultSettingValue = cfgMethod.invoke(DEFAULT_CFG);
-            Object assignedValue = SETTING_ASSIGNMENTS.get(propName);
-            assertNotEquals("SETTING_ASSIGNMENTS must contain a non-default value for " + propName,
-                    assignedValue, defaultSettingValue);
-
-            TemplateConfigurer tc = new TemplateConfigurer();
-            try {
-                pd.getWriteMethod().invoke(tc, assignedValue);
-            } catch (Exception e) {
-                throw new IllegalStateException("For setting \"" + propName + "\" and assigned value of type "
-                        + (assignedValue != null ? assignedValue.getClass().getName() : "Null"),
-                        e);
-            }
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/core/TemplateConfigurerWithTemplateCacheTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/TemplateConfigurerWithTemplateCacheTest.java b/src/test/java/freemarker/core/TemplateConfigurerWithTemplateCacheTest.java
deleted file mode 100644
index 53e8193..0000000
--- a/src/test/java/freemarker/core/TemplateConfigurerWithTemplateCacheTest.java
+++ /dev/null
@@ -1,325 +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 freemarker.core;
-
-import static org.junit.Assert.*;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.Locale;
-
-import org.junit.Test;
-
-import freemarker.cache.ByteArrayTemplateLoader;
-import freemarker.cache.ConditionalTemplateConfigurerFactory;
-import freemarker.cache.FileNameGlobMatcher;
-import freemarker.cache.FirstMatchTemplateConfigurerFactory;
-import freemarker.cache.MergingTemplateConfigurerFactory;
-import freemarker.cache.StringTemplateLoader;
-import freemarker.template.Configuration;
-import freemarker.template.Template;
-import freemarker.template.TemplateException;
-
-public class TemplateConfigurerWithTemplateCacheTest {
-
-    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_2_3_23);
-        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);
-
-        TemplateConfigurer tcDe = new TemplateConfigurer();
-        tcDe.setLocale(Locale.GERMANY);
-        cfg.setTemplateConfigurers(new ConditionalTemplateConfigurerFactory(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_2_3_22);
-        
-        TemplateConfigurer tcDE = new TemplateConfigurer();
-        tcDE.setLocale(Locale.GERMANY);
-        TemplateConfigurer tcYN = new TemplateConfigurer();
-        tcYN.setBooleanFormat("Y,N");
-        cfg.setTemplateConfigurers(
-                    new MergingTemplateConfigurerFactory(
-                            cfg.getTemplateConfigurers(),
-                            new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("utf16.ftl"), tcDE),
-                            new ConditionalTemplateConfigurerFactory(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_2_3_22);
-        cfg.setLocale(Locale.US);
-        
-        TemplateConfigurer tcFR = new TemplateConfigurer();
-        tcFR.setLocale(Locale.FRANCE);
-        TemplateConfigurer tcYN = new TemplateConfigurer();
-        tcYN.setBooleanFormat("Y,N");
-        TemplateConfigurer tc00 = new TemplateConfigurer();
-        tc00.setNumberFormat("0.00");
-        cfg.setTemplateConfigurers(
-                new MergingTemplateConfigurerFactory(
-                        new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*(fr)*"), tcFR),
-                        new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*(yn)*"), tcYN),
-                        new ConditionalTemplateConfigurerFactory(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_2_3_22);
-        
-        TemplateConfigurer tc1 = new TemplateConfigurer();
-        tc1.setCustomAttribute("a1", "a1tc1");
-        tc1.setCustomAttribute("a2", "a2tc1");
-        tc1.setCustomAttribute("a3", "a3tc1");
-        CUST_ATT_1.set("ca1tc1", tc1);
-        CUST_ATT_2.set("ca2tc1", tc1);
-        
-        TemplateConfigurer tc2 = new TemplateConfigurer();
-        tc2.setCustomAttribute("a1", "a1tc2");
-        CUST_ATT_1.set("ca1tc2", tc2);
-        
-        cfg.setTemplateConfigurers(
-                new MergingTemplateConfigurerFactory(
-                        new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*(tc1)*"), tc1),
-                        new ConditionalTemplateConfigurerFactory(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_2_3_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);
-        
-        TemplateConfigurer tcUtf8 = new TemplateConfigurer();
-        tcUtf8.setEncoding("utf-8");
-        TemplateConfigurer tcUtf16 = new TemplateConfigurer();
-        tcUtf16.setEncoding("utf-16");
-        cfg.setTemplateConfigurers(
-                new FirstMatchTemplateConfigurerFactory(
-                        new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*utf8*"), tcUtf8),
-                        new ConditionalTemplateConfigurerFactory(new FileNameGlobMatcher("*utf16*"), tcUtf16)
-                ).allowNoMatch(true));
-        return cfg;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java b/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java
index f008280..0e9f539 100644
--- a/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java
+++ b/src/test/java/freemarker/ext/servlet/FreemarkerServletTest.java
@@ -38,13 +38,13 @@ import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.mock.web.MockServletConfig;
 import org.springframework.mock.web.MockServletContext;
 
-import freemarker.cache.ConditionalTemplateConfigurerFactory;
+import freemarker.cache.ConditionalTemplateConfigurationFactory;
 import freemarker.cache.FileNameGlobMatcher;
-import freemarker.cache.FirstMatchTemplateConfigurerFactory;
+import freemarker.cache.FirstMatchTemplateConfigurationFactory;
 import freemarker.cache.StringTemplateLoader;
 import freemarker.cache.TemplateLoader;
 import freemarker.core.Environment;
-import freemarker.core.TemplateConfigurer;
+import freemarker.core.TemplateConfiguration;
 import freemarker.template.Configuration;
 import freemarker.template.Template;
 import freemarker.template.TemplateException;
@@ -491,7 +491,7 @@ public class FreemarkerServletTest {
         @Override
         protected Configuration createConfiguration() {
             Configuration cfg = super.createConfiguration();
-            // Needed for the TemplateConfigurer that sets outputEncoding:
+            // Needed for the TemplateConfiguration that sets outputEncoding:
             cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_22);
 
             // Set a test runner environment independent default locale:
@@ -499,17 +499,17 @@ public class FreemarkerServletTest {
             cfg.setDefaultEncoding(CFG_DEFAULT_ENCODING);
 
             {
-                TemplateConfigurer outUtf8TC = new TemplateConfigurer();
+                TemplateConfiguration outUtf8TC = new TemplateConfiguration();
                 outUtf8TC.setOutputEncoding("UTF-8");
                 
-                TemplateConfigurer srcUtf8TC = new TemplateConfigurer();
+                TemplateConfiguration srcUtf8TC = new TemplateConfiguration();
                 srcUtf8TC.setEncoding("UTF-8");
                 
-                cfg.setTemplateConfigurers(
-                        new FirstMatchTemplateConfigurerFactory(
-                                new ConditionalTemplateConfigurerFactory(
+                cfg.setTemplateConfigurations(
+                        new FirstMatchTemplateConfigurationFactory(
+                                new ConditionalTemplateConfigurationFactory(
                                         new FileNameGlobMatcher(FOO_SRC_UTF8_FTL), srcUtf8TC),
-                                new ConditionalTemplateConfigurerFactory(
+                                new ConditionalTemplateConfigurationFactory(
                                         new FileNameGlobMatcher(FOO_OUT_UTF8_FTL), outUtf8TC)
                         )
                         .allowNoMatch(true)

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/manual/ConfigureOutputFormatExamples.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/manual/ConfigureOutputFormatExamples.java b/src/test/java/freemarker/manual/ConfigureOutputFormatExamples.java
index 5f5fb9f..8aa6ff4 100644
--- a/src/test/java/freemarker/manual/ConfigureOutputFormatExamples.java
+++ b/src/test/java/freemarker/manual/ConfigureOutputFormatExamples.java
@@ -22,14 +22,14 @@ import static org.junit.Assert.*;
 
 import org.junit.Test;
 
-import freemarker.cache.ConditionalTemplateConfigurerFactory;
+import freemarker.cache.ConditionalTemplateConfigurationFactory;
 import freemarker.cache.FileExtensionMatcher;
-import freemarker.cache.FirstMatchTemplateConfigurerFactory;
+import freemarker.cache.FirstMatchTemplateConfigurationFactory;
 import freemarker.cache.OrMatcher;
 import freemarker.cache.PathGlobMatcher;
 import freemarker.core.HTMLOutputFormat;
 import freemarker.core.RTFOutputFormat;
-import freemarker.core.TemplateConfigurer;
+import freemarker.core.TemplateConfiguration;
 import freemarker.core.XMLOutputFormat;
 import freemarker.template.Configuration;
 
@@ -47,11 +47,11 @@ public class ConfigureOutputFormatExamples extends ExamplesTest {
         
         // Example 2/a:
         {
-            TemplateConfigurer tcHTML = new TemplateConfigurer();
+            TemplateConfiguration tcHTML = new TemplateConfiguration();
             tcHTML.setOutputFormat(HTMLOutputFormat.INSTANCE);
             
-            cfg.setTemplateConfigurers(
-                    new ConditionalTemplateConfigurerFactory(
+            cfg.setTemplateConfigurations(
+                    new ConditionalTemplateConfigurationFactory(
                             new PathGlobMatcher("mail/**"),
                             tcHTML));
             
@@ -60,7 +60,7 @@ public class ConfigureOutputFormatExamples extends ExamplesTest {
 
         // Example 2/b:
         {
-            cfg.setTemplateConfigurers(null); // Just to be sure...
+            cfg.setTemplateConfigurations(null); // Just to be sure...
             
             cfg.setSettings(loadPropertiesFile("ConfigureOutputFormatExamples1.properties"));
                 
@@ -69,26 +69,26 @@ public class ConfigureOutputFormatExamples extends ExamplesTest {
         
         // Example 3/a:
         {
-            TemplateConfigurer tcHTML = new TemplateConfigurer();
+            TemplateConfiguration tcHTML = new TemplateConfiguration();
             tcHTML.setOutputFormat(HTMLOutputFormat.INSTANCE);
             
-            TemplateConfigurer tcXML = new TemplateConfigurer();
+            TemplateConfiguration tcXML = new TemplateConfiguration();
             tcXML.setOutputFormat(XMLOutputFormat.INSTANCE);
 
-            TemplateConfigurer tcRTF = new TemplateConfigurer();
+            TemplateConfiguration tcRTF = new TemplateConfiguration();
             tcRTF.setOutputFormat(RTFOutputFormat.INSTANCE);
             
-            cfg.setTemplateConfigurers(
-                    new FirstMatchTemplateConfigurerFactory(
-                            new ConditionalTemplateConfigurerFactory(
+            cfg.setTemplateConfigurations(
+                    new FirstMatchTemplateConfigurationFactory(
+                            new ConditionalTemplateConfigurationFactory(
                                     new FileExtensionMatcher("xml"),
                                     tcXML),
-                            new ConditionalTemplateConfigurerFactory(
+                            new ConditionalTemplateConfigurationFactory(
                                     new OrMatcher(
                                             new FileExtensionMatcher("html"),
                                             new FileExtensionMatcher("htm")),
                                     tcHTML),
-                            new ConditionalTemplateConfigurerFactory(
+                            new ConditionalTemplateConfigurationFactory(
                                     new FileExtensionMatcher("rtf"),
                                     tcRTF)
                     ).allowNoMatch(true)
@@ -102,7 +102,7 @@ public class ConfigureOutputFormatExamples extends ExamplesTest {
 
         // Example 3/b:
         {
-            cfg.setTemplateConfigurers(null); // Just to be sure...
+            cfg.setTemplateConfigurations(null); // Just to be sure...
             
             cfg.setSettings(loadPropertiesFile("ConfigureOutputFormatExamples2.properties"));
             

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/manual/TemplateConfigurationExamples.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/manual/TemplateConfigurationExamples.java b/src/test/java/freemarker/manual/TemplateConfigurationExamples.java
new file mode 100644
index 0000000..97e16f9
--- /dev/null
+++ b/src/test/java/freemarker/manual/TemplateConfigurationExamples.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package freemarker.manual;
+
+import static org.junit.Assert.*;
+
+import java.util.Date;
+
+import org.junit.Test;
+
+import freemarker.cache.ConditionalTemplateConfigurationFactory;
+import freemarker.cache.FileExtensionMatcher;
+import freemarker.cache.FileNameGlobMatcher;
+import freemarker.cache.FirstMatchTemplateConfigurationFactory;
+import freemarker.cache.MergingTemplateConfigurationFactory;
+import freemarker.cache.OrMatcher;
+import freemarker.cache.PathGlobMatcher;
+import freemarker.core.HTMLOutputFormat;
+import freemarker.core.PlainTextOutputFormat;
+import freemarker.core.TemplateConfiguration;
+import freemarker.core.UndefinedOutputFormat;
+import freemarker.core.XMLOutputFormat;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.utility.DateUtil;
+
+public class TemplateConfigurationExamples extends ExamplesTest {
+
+    @Test
+    public void example1() throws Exception {
+        Configuration cfg = getConfiguration();
+
+        addTemplate("t.xml", "");
+        
+        TemplateConfiguration tcUTF8XML = new TemplateConfiguration();
+        tcUTF8XML.setEncoding("utf-8");
+        tcUTF8XML.setOutputFormat(XMLOutputFormat.INSTANCE);
+
+        {
+            cfg.setTemplateConfigurations(new ConditionalTemplateConfigurationFactory(
+                    new FileExtensionMatcher("xml"), tcUTF8XML));
+            
+            Template t = cfg.getTemplate("t.xml");
+            assertEquals("utf-8", t.getEncoding());
+            assertEquals(XMLOutputFormat.INSTANCE, t.getOutputFormat());
+        }
+
+        {
+            cfg.setTemplateConfigurations(null);
+            cfg.setSettings(loadPropertiesFile("TemplateConfigurationExamples1.properties"));
+            
+            Template t = cfg.getTemplate("t.xml");
+            assertEquals("utf-8", t.getEncoding());
+            assertEquals(XMLOutputFormat.INSTANCE, t.getOutputFormat());
+        }
+    }
+
+    @Test
+    public void example2() throws Exception {
+        Configuration cfg = getConfiguration();
+        
+        addTemplate("t.subject.ftl", "");
+        addTemplate("mail/t.subject.ftl", "");
+        addTemplate("mail/t.body.ftl", "");
+
+        TemplateConfiguration tcSubject = new TemplateConfiguration();
+        tcSubject.setOutputFormat(PlainTextOutputFormat.INSTANCE);
+        
+        TemplateConfiguration tcBody = new TemplateConfiguration();
+        tcBody.setOutputFormat(HTMLOutputFormat.INSTANCE);
+        
+        cfg.setTemplateConfigurations(
+                new ConditionalTemplateConfigurationFactory(
+                        new PathGlobMatcher("mail/**"),
+                        new FirstMatchTemplateConfigurationFactory(
+                                new ConditionalTemplateConfigurationFactory(
+                                        new FileNameGlobMatcher("*.subject.*"),
+                                        tcSubject),
+                                new ConditionalTemplateConfigurationFactory(
+                                        new FileNameGlobMatcher("*.body.*"),
+                                        tcBody)
+                                )
+                                .noMatchErrorDetails("Mail template names must contain \".subject.\" or \".body.\"!")
+                        ));
+        
+        assertEquals(UndefinedOutputFormat.INSTANCE, cfg.getTemplate("t.subject.ftl").getOutputFormat());
+        assertEquals(PlainTextOutputFormat.INSTANCE, cfg.getTemplate("mail/t.subject.ftl").getOutputFormat());
+        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("mail/t.body.ftl").getOutputFormat());
+        
+        // From properties:
+        
+        cfg.setTemplateConfigurations(null);
+        cfg.setSettings(loadPropertiesFile("TemplateConfigurationExamples2.properties"));
+        
+        assertEquals(UndefinedOutputFormat.INSTANCE, cfg.getTemplate("t.subject.ftl").getOutputFormat());
+        assertEquals(PlainTextOutputFormat.INSTANCE, cfg.getTemplate("mail/t.subject.ftl").getOutputFormat());
+        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("mail/t.body.ftl").getOutputFormat());
+    };
+    
+    @Test
+    public void example3() throws Exception {
+        Configuration cfg = getConfiguration();
+        cfg.setDefaultEncoding("ISO-8859-1");
+        cfg.setSharedVariable("ts", new Date(1440431606011L));
+        
+        addTemplate("t.stats.html", "${ts?datetime} ${ts?date} ${ts?time}");
+        addTemplate("t.html", "");
+        addTemplate("t.htm", "");
+        addTemplate("t.xml", "");
+        addTemplate("mail/t.html", "");
+
+        TemplateConfiguration tcStats = new TemplateConfiguration();
+        tcStats.setDateTimeFormat("iso");
+        tcStats.setDateFormat("iso");
+        tcStats.setTimeFormat("iso");
+        tcStats.setTimeZone(DateUtil.UTC);
+
+        TemplateConfiguration tcMail = new TemplateConfiguration();
+        tcMail.setEncoding("utf-8");
+        
+        TemplateConfiguration tcHTML = new TemplateConfiguration();
+        tcHTML.setOutputFormat(HTMLOutputFormat.INSTANCE);
+        
+        TemplateConfiguration tcXML = new TemplateConfiguration();
+        tcXML.setOutputFormat(XMLOutputFormat.INSTANCE);
+        
+        cfg.setTemplateConfigurations(
+                new MergingTemplateConfigurationFactory(
+                        new ConditionalTemplateConfigurationFactory(
+                                new FileNameGlobMatcher("*.stats.*"),
+                                tcStats),
+                        new ConditionalTemplateConfigurationFactory(
+                                new PathGlobMatcher("mail/**"),
+                                tcMail),
+                        new FirstMatchTemplateConfigurationFactory(
+                                new ConditionalTemplateConfigurationFactory(
+                                        new FileExtensionMatcher("xml"),
+                                        tcXML),
+                                new ConditionalTemplateConfigurationFactory(
+                                        new OrMatcher(
+                                                new FileExtensionMatcher("html"),
+                                                new FileExtensionMatcher("htm")),
+                                        tcHTML)
+                        ).allowNoMatch(true)
+                )
+        );
+        
+        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.html").getOutputFormat());
+        assertEquals("ISO-8859-1", cfg.getTemplate("t.html").getEncoding());
+        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.htm").getOutputFormat());
+        assertEquals(XMLOutputFormat.INSTANCE, cfg.getTemplate("t.xml").getOutputFormat());
+        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.stats.html").getOutputFormat());
+        assertOutputForNamed("t.stats.html", "2015-08-24T15:53:26.011Z 2015-08-24 15:53:26.011Z");
+        assertEquals("utf-8", cfg.getTemplate("mail/t.html").getEncoding());
+        
+        // From properties:
+        
+        cfg.setTemplateConfigurations(null);
+        cfg.setSettings(loadPropertiesFile("TemplateConfigurationExamples3.properties"));
+        
+        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.html").getOutputFormat());
+        assertEquals("ISO-8859-1", cfg.getTemplate("t.html").getEncoding());
+        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.htm").getOutputFormat());
+        assertEquals(XMLOutputFormat.INSTANCE, cfg.getTemplate("t.xml").getOutputFormat());
+        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.stats.html").getOutputFormat());
+        assertOutputForNamed("t.stats.html", "2015-08-24T15:53:26.011Z 2015-08-24 15:53:26.011Z");
+        assertEquals("utf-8", cfg.getTemplate("mail/t.html").getEncoding());
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/manual/TemplateConfigurerExamples.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/manual/TemplateConfigurerExamples.java b/src/test/java/freemarker/manual/TemplateConfigurerExamples.java
deleted file mode 100644
index 8e3844d..0000000
--- a/src/test/java/freemarker/manual/TemplateConfigurerExamples.java
+++ /dev/null
@@ -1,186 +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 freemarker.manual;
-
-import static org.junit.Assert.*;
-
-import java.util.Date;
-
-import org.junit.Test;
-
-import freemarker.cache.ConditionalTemplateConfigurerFactory;
-import freemarker.cache.FileExtensionMatcher;
-import freemarker.cache.FileNameGlobMatcher;
-import freemarker.cache.FirstMatchTemplateConfigurerFactory;
-import freemarker.cache.MergingTemplateConfigurerFactory;
-import freemarker.cache.OrMatcher;
-import freemarker.cache.PathGlobMatcher;
-import freemarker.core.HTMLOutputFormat;
-import freemarker.core.PlainTextOutputFormat;
-import freemarker.core.TemplateConfigurer;
-import freemarker.core.UndefinedOutputFormat;
-import freemarker.core.XMLOutputFormat;
-import freemarker.template.Configuration;
-import freemarker.template.Template;
-import freemarker.template.utility.DateUtil;
-
-public class TemplateConfigurerExamples extends ExamplesTest {
-
-    @Test
-    public void example1() throws Exception {
-        Configuration cfg = getConfiguration();
-
-        addTemplate("t.xml", "");
-        
-        TemplateConfigurer tcUTF8XML = new TemplateConfigurer();
-        tcUTF8XML.setEncoding("utf-8");
-        tcUTF8XML.setOutputFormat(XMLOutputFormat.INSTANCE);
-
-        {
-            cfg.setTemplateConfigurers(new ConditionalTemplateConfigurerFactory(
-                    new FileExtensionMatcher("xml"), tcUTF8XML));
-            
-            Template t = cfg.getTemplate("t.xml");
-            assertEquals("utf-8", t.getEncoding());
-            assertEquals(XMLOutputFormat.INSTANCE, t.getOutputFormat());
-        }
-
-        {
-            cfg.setTemplateConfigurers(null);
-            cfg.setSettings(loadPropertiesFile("TemplateConfigurerExamples1.properties"));
-            
-            Template t = cfg.getTemplate("t.xml");
-            assertEquals("utf-8", t.getEncoding());
-            assertEquals(XMLOutputFormat.INSTANCE, t.getOutputFormat());
-        }
-    }
-
-    @Test
-    public void example2() throws Exception {
-        Configuration cfg = getConfiguration();
-        
-        addTemplate("t.subject.ftl", "");
-        addTemplate("mail/t.subject.ftl", "");
-        addTemplate("mail/t.body.ftl", "");
-
-        TemplateConfigurer tcSubject = new TemplateConfigurer();
-        tcSubject.setOutputFormat(PlainTextOutputFormat.INSTANCE);
-        
-        TemplateConfigurer tcBody = new TemplateConfigurer();
-        tcBody.setOutputFormat(HTMLOutputFormat.INSTANCE);
-        
-        cfg.setTemplateConfigurers(
-                new ConditionalTemplateConfigurerFactory(
-                        new PathGlobMatcher("mail/**"),
-                        new FirstMatchTemplateConfigurerFactory(
-                                new ConditionalTemplateConfigurerFactory(
-                                        new FileNameGlobMatcher("*.subject.*"),
-                                        tcSubject),
-                                new ConditionalTemplateConfigurerFactory(
-                                        new FileNameGlobMatcher("*.body.*"),
-                                        tcBody)
-                                )
-                                .noMatchErrorDetails("Mail template names must contain \".subject.\" or \".body.\"!")
-                        ));
-        
-        assertEquals(UndefinedOutputFormat.INSTANCE, cfg.getTemplate("t.subject.ftl").getOutputFormat());
-        assertEquals(PlainTextOutputFormat.INSTANCE, cfg.getTemplate("mail/t.subject.ftl").getOutputFormat());
-        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("mail/t.body.ftl").getOutputFormat());
-        
-        // From properties:
-        
-        cfg.setTemplateConfigurers(null);
-        cfg.setSettings(loadPropertiesFile("TemplateConfigurerExamples2.properties"));
-        
-        assertEquals(UndefinedOutputFormat.INSTANCE, cfg.getTemplate("t.subject.ftl").getOutputFormat());
-        assertEquals(PlainTextOutputFormat.INSTANCE, cfg.getTemplate("mail/t.subject.ftl").getOutputFormat());
-        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("mail/t.body.ftl").getOutputFormat());
-    };
-    
-    @Test
-    public void example3() throws Exception {
-        Configuration cfg = getConfiguration();
-        cfg.setDefaultEncoding("ISO-8859-1");
-        cfg.setSharedVariable("ts", new Date(1440431606011L));
-        
-        addTemplate("t.stats.html", "${ts?datetime} ${ts?date} ${ts?time}");
-        addTemplate("t.html", "");
-        addTemplate("t.htm", "");
-        addTemplate("t.xml", "");
-        addTemplate("mail/t.html", "");
-
-        TemplateConfigurer tcStats = new TemplateConfigurer();
-        tcStats.setDateTimeFormat("iso");
-        tcStats.setDateFormat("iso");
-        tcStats.setTimeFormat("iso");
-        tcStats.setTimeZone(DateUtil.UTC);
-
-        TemplateConfigurer tcMail = new TemplateConfigurer();
-        tcMail.setEncoding("utf-8");
-        
-        TemplateConfigurer tcHTML = new TemplateConfigurer();
-        tcHTML.setOutputFormat(HTMLOutputFormat.INSTANCE);
-        
-        TemplateConfigurer tcXML = new TemplateConfigurer();
-        tcXML.setOutputFormat(XMLOutputFormat.INSTANCE);
-        
-        cfg.setTemplateConfigurers(
-                new MergingTemplateConfigurerFactory(
-                        new ConditionalTemplateConfigurerFactory(
-                                new FileNameGlobMatcher("*.stats.*"),
-                                tcStats),
-                        new ConditionalTemplateConfigurerFactory(
-                                new PathGlobMatcher("mail/**"),
-                                tcMail),
-                        new FirstMatchTemplateConfigurerFactory(
-                                new ConditionalTemplateConfigurerFactory(
-                                        new FileExtensionMatcher("xml"),
-                                        tcXML),
-                                new ConditionalTemplateConfigurerFactory(
-                                        new OrMatcher(
-                                                new FileExtensionMatcher("html"),
-                                                new FileExtensionMatcher("htm")),
-                                        tcHTML)
-                        ).allowNoMatch(true)
-                )
-        );
-        
-        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.html").getOutputFormat());
-        assertEquals("ISO-8859-1", cfg.getTemplate("t.html").getEncoding());
-        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.htm").getOutputFormat());
-        assertEquals(XMLOutputFormat.INSTANCE, cfg.getTemplate("t.xml").getOutputFormat());
-        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.stats.html").getOutputFormat());
-        assertOutputForNamed("t.stats.html", "2015-08-24T15:53:26.011Z 2015-08-24 15:53:26.011Z");
-        assertEquals("utf-8", cfg.getTemplate("mail/t.html").getEncoding());
-        
-        // From properties:
-        
-        cfg.setTemplateConfigurers(null);
-        cfg.setSettings(loadPropertiesFile("TemplateConfigurerExamples3.properties"));
-        
-        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.html").getOutputFormat());
-        assertEquals("ISO-8859-1", cfg.getTemplate("t.html").getEncoding());
-        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.htm").getOutputFormat());
-        assertEquals(XMLOutputFormat.INSTANCE, cfg.getTemplate("t.xml").getOutputFormat());
-        assertEquals(HTMLOutputFormat.INSTANCE, cfg.getTemplate("t.stats.html").getOutputFormat());
-        assertOutputForNamed("t.stats.html", "2015-08-24T15:53:26.011Z 2015-08-24 15:53:26.011Z");
-        assertEquals("utf-8", cfg.getTemplate("mail/t.html").getEncoding());
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/java/freemarker/template/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/template/ConfigurationTest.java b/src/test/java/freemarker/template/ConfigurationTest.java
index b7de38e..9e69d90 100644
--- a/src/test/java/freemarker/template/ConfigurationTest.java
+++ b/src/test/java/freemarker/template/ConfigurationTest.java
@@ -66,7 +66,6 @@ import freemarker.core.HTMLOutputFormat;
 import freemarker.core.HexTemplateNumberFormatFactory;
 import freemarker.core.MarkupOutputFormat;
 import freemarker.core.OutputFormat;
-import freemarker.core.ParseException;
 import freemarker.core.RTFOutputFormat;
 import freemarker.core.TemplateDateFormatFactory;
 import freemarker.core.TemplateNumberFormatFactory;
@@ -812,9 +811,9 @@ public class ConfigurationTest extends TestCase {
         assertEquals(0, cache.getSize());
     }
     
-    public void testSetTemplateConfigurers() throws TemplateException, TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException {
+    public void testSetTemplateConfigurations() throws Exception {
         Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
-        assertNull(cfg.getTemplateConfigurers());
+        assertNull(cfg.getTemplateConfigurations());
 
         StringTemplateLoader tl = new StringTemplateLoader();
         tl.putTemplate("t.de.ftlh", "");
@@ -825,25 +824,25 @@ public class ConfigurationTest extends TestCase {
         
         cfg.setTimeZone(TimeZone.getTimeZone("GMT+09"));
         
-        cfg.setSetting(Configuration.TEMPLATE_CONFIGURERS_KEY,
-                "MergingTemplateConfigurerFactory("
-                    + "FirstMatchTemplateConfigurerFactory("
-                        + "ConditionalTemplateConfigurerFactory("
-                            + "FileNameGlobMatcher('*.de.*'), TemplateConfigurer(timeZone=TimeZone('GMT+01'))), "
-                        + "ConditionalTemplateConfigurerFactory("
-                            + "FileNameGlobMatcher('*.fr.*'), TemplateConfigurer(timeZone=TimeZone('GMT'))), "
+        cfg.setSetting(Configuration.TEMPLATE_CONFIGURATIONS_KEY,
+                "MergingTemplateConfigurationFactory("
+                    + "FirstMatchTemplateConfigurationFactory("
+                        + "ConditionalTemplateConfigurationFactory("
+                            + "FileNameGlobMatcher('*.de.*'), TemplateConfiguration(timeZone=TimeZone('GMT+01'))), "
+                        + "ConditionalTemplateConfigurationFactory("
+                            + "FileNameGlobMatcher('*.fr.*'), TemplateConfiguration(timeZone=TimeZone('GMT'))), "
                         + "allowNoMatch=true"
                     + "), "
-                    + "FirstMatchTemplateConfigurerFactory("
-                        + "ConditionalTemplateConfigurerFactory("
-                            + "FileExtensionMatcher('ftlh'), TemplateConfigurer(booleanFormat='TODO,HTML')), "
-                        + "ConditionalTemplateConfigurerFactory("
-                            + "FileExtensionMatcher('ftlx'), TemplateConfigurer(booleanFormat='TODO,XML')), "
+                    + "FirstMatchTemplateConfigurationFactory("
+                        + "ConditionalTemplateConfigurationFactory("
+                            + "FileExtensionMatcher('ftlh'), TemplateConfiguration(booleanFormat='TODO,HTML')), "
+                        + "ConditionalTemplateConfigurationFactory("
+                            + "FileExtensionMatcher('ftlx'), TemplateConfiguration(booleanFormat='TODO,XML')), "
                         + "noMatchErrorDetails='Unrecognized template file extension'"
                     + "), "
-                    + "ConditionalTemplateConfigurerFactory("
+                    + "ConditionalTemplateConfigurationFactory("
                         + "PathGlobMatcher('stat/**', caseInsensitive=true), "
-                        + "TemplateConfigurer(timeZone=TimeZone('UTC'))"
+                        + "TemplateConfiguration(timeZone=TimeZone('UTC'))"
                     + ")"
                 + ")");
         
@@ -868,9 +867,9 @@ public class ConfigurationTest extends TestCase {
             assertEquals(DateUtil.UTC, t.getTimeZone());
         }
         
-        assertNotNull(cfg.getTemplateConfigurers());
-        cfg.setSetting(Configuration.TEMPLATE_CONFIGURERS_KEY, "null");
-        assertNull(cfg.getTemplateConfigurers());
+        assertNotNull(cfg.getTemplateConfigurations());
+        cfg.setSetting(Configuration.TEMPLATE_CONFIGURATIONS_KEY, "null");
+        assertNull(cfg.getTemplateConfigurations());
     }
 
     public void testSetAutoEscaping() throws Exception {
@@ -1013,7 +1012,7 @@ public class ConfigurationTest extends TestCase {
                 new ArrayList(cfg.getRegisteredCustomOutputFormats()));
         
         try {
-            cfg.setSetting(Configuration.REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE, "[TemplateConfigurer()]");
+            cfg.setSetting(Configuration.REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE, "[TemplateConfiguration()]");
             fail();
         } catch (Exception e) {
             assertThat(e.getCause().getMessage(), containsString(OutputFormat.class.getSimpleName()));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples1.properties
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples1.properties b/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples1.properties
index 0f59312..f21272a 100644
--- a/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples1.properties
+++ b/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples1.properties
@@ -1,4 +1,4 @@
-templateConfigurers = \
-    ConditionalTemplateConfigurerFactory( \
+templateConfigurations = \
+    ConditionalTemplateConfigurationFactory( \
         PathGlobMatcher("mail/**"), \
-        TemplateConfigurer(outputFormat = HTMLOutputFormat()))
\ No newline at end of file
+        TemplateConfiguration(outputFormat = HTMLOutputFormat()))
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples2.properties
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples2.properties b/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples2.properties
index 23fcee3..b01090b 100644
--- a/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples2.properties
+++ b/src/test/resources/freemarker/manual/ConfigureOutputFormatExamples2.properties
@@ -1,14 +1,14 @@
-templateConfigurers = \
-    FirstMatchTemplateConfigurerFactory( \
-        ConditionalTemplateConfigurerFactory( \
+templateConfigurations = \
+    FirstMatchTemplateConfigurationFactory( \
+        ConditionalTemplateConfigurationFactory( \
             FileExtensionMatcher("xml"), \
-            TemplateConfigurer(outputFormat = XMLOutputFormat())), \
-        ConditionalTemplateConfigurerFactory( \
+            TemplateConfiguration(outputFormat = XMLOutputFormat())), \
+        ConditionalTemplateConfigurationFactory( \
             OrMatcher( \
                 FileExtensionMatcher("html"), \
                 FileExtensionMatcher("htm")), \
-            TemplateConfigurer(outputFormat = HTMLOutputFormat())), \
-        ConditionalTemplateConfigurerFactory( \
+            TemplateConfiguration(outputFormat = HTMLOutputFormat())), \
+        ConditionalTemplateConfigurationFactory( \
             FileExtensionMatcher("rtf"), \
-            TemplateConfigurer(outputFormat = RTFOutputFormat())), \
+            TemplateConfiguration(outputFormat = RTFOutputFormat())), \
         allowNoMatch = true)

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/resources/freemarker/manual/TemplateConfigurationExamples1.properties
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/TemplateConfigurationExamples1.properties b/src/test/resources/freemarker/manual/TemplateConfigurationExamples1.properties
new file mode 100644
index 0000000..b6edf1a
--- /dev/null
+++ b/src/test/resources/freemarker/manual/TemplateConfigurationExamples1.properties
@@ -0,0 +1,8 @@
+templateConfigurations = \
+    ConditionalTemplateConfigurationFactory( \
+        FileExtensionMatcher("xml"), \
+        TemplateConfiguration( \
+            encoding = "utf-8", \
+            outputFormat = XMLOutputFormat() \
+        ) \
+    )
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/resources/freemarker/manual/TemplateConfigurationExamples2.properties
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/TemplateConfigurationExamples2.properties b/src/test/resources/freemarker/manual/TemplateConfigurationExamples2.properties
new file mode 100644
index 0000000..a39acca
--- /dev/null
+++ b/src/test/resources/freemarker/manual/TemplateConfigurationExamples2.properties
@@ -0,0 +1,15 @@
+templateConfigurations = \
+    ConditionalTemplateConfigurationFactory( \
+        PathGlobMatcher("mail/**"), \
+        FirstMatchTemplateConfigurationFactory( \
+            ConditionalTemplateConfigurationFactory( \
+                FileNameGlobMatcher("*.subject.*"), \
+                TemplateConfiguration(outputFormat = PlainTextOutputFormat()) \
+            ), \
+            ConditionalTemplateConfigurationFactory( \
+                FileNameGlobMatcher("*.body.*"), \
+                TemplateConfiguration(outputFormat = HTMLOutputFormat()) \
+            ), \
+            noMatchErrorDetails = 'Mail template names must contain ".subject." or ".body."!' \
+        ) \
+    )

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/test/resources/freemarker/manual/TemplateConfigurationExamples3.properties
----------------------------------------------------------------------
diff --git a/src/test/resources/freemarker/manual/TemplateConfigurationExamples3.properties b/src/test/resources/freemarker/manual/TemplateConfigurationExamples3.properties
new file mode 100644
index 0000000..424330b
--- /dev/null
+++ b/src/test/resources/freemarker/manual/TemplateConfigurationExamples3.properties
@@ -0,0 +1,30 @@
+templateConfigurations = \
+    MergingTemplateConfigurationFactory( \
+        ConditionalTemplateConfigurationFactory( \
+            FileNameGlobMatcher("*.stats.*"), \
+            TemplateConfiguration( \
+                dateTimeFormat = "iso", \
+                dateFormat = "iso", \
+                timeFormat = "iso", \
+                timeZone = TimeZone("UTC") \
+            ) \
+        ), \
+        ConditionalTemplateConfigurationFactory( \
+            PathGlobMatcher("mail/**"), \
+            TemplateConfiguration(encoding = "utf-8") \
+        ), \
+        FirstMatchTemplateConfigurationFactory( \
+            ConditionalTemplateConfigurationFactory( \
+                FileExtensionMatcher("xml"), \
+                TemplateConfiguration(outputFormat = XMLOutputFormat()) \
+            ), \
+            ConditionalTemplateConfigurationFactory( \
+                OrMatcher( \
+                    FileExtensionMatcher("html"), \
+                    FileExtensionMatcher("htm") \
+                ), \
+                TemplateConfiguration(outputFormat = HTMLOutputFormat()) \
+            ), \
+            allowNoMatch = true \
+        ) \
+    )



[5/7] incubator-freemarker git commit: The new (in 2.3.24-pre01) TemplateConfigurer class was renamed to TemplateConfiguration, and the related configuration setting from template_configurers to template_configurations. Also, the TemplateConfigurer.confi

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/core/TemplateConfigurer.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateConfigurer.java b/src/main/java/freemarker/core/TemplateConfigurer.java
deleted file mode 100644
index 78c627f..0000000
--- a/src/main/java/freemarker/core/TemplateConfigurer.java
+++ /dev/null
@@ -1,580 +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 freemarker.core;
-
-import java.io.Reader;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import freemarker.cache.TemplateCache;
-import freemarker.template.Configuration;
-import freemarker.template.Template;
-import freemarker.template.Version;
-import freemarker.template._TemplateAPI;
-import freemarker.template.utility.NullArgumentException;
-
-/**
- * Used for customizing the configuration settings for individual {@link Template}-s, relatively to the common setting
- * values coming from the {@link Configuration}. This was designed with the standard template loading mechanism of
- * FreeMarker in mind ({@link Configuration#getTemplate(String)} and {@link TemplateCache}), though can also be reused
- * for custom template loading and caching solutions.
- * 
- * <p>
- * Note on the {@code locale} setting: When used with the standard template loading/caching mechanism
- * ({@link Configuration#getTemplate(String)} and its overloads), localized lookup happens before the {@code locale}
- * specified here could have effect. The {@code locale} will be only set in the template that the localized looks has
- * already found.
- * 
- * <p>
- * Note on encoding setting {@code encoding}: See {@link #setEncoding(String)}.
- * 
- * <p>
- * Note that the result value of the reader methods (getter and "is" methods) is usually not useful unless the value of
- * that setting was already set on this object. Otherwise you will get the value from the parent {@link Configuration},
- * which is {@link Configuration#getDefaultConfiguration()} before this object is associated to a {@link Configuration}.
- * 
- * <p>
- * If you are using this class for your own template loading and caching solution, rather than with the standard one,
- * you should be aware of a few more details:
- * 
- * <ul>
- * <li>This class implements both {@link Configurable} and
- * {@link ParserConfiguration}. This means that it can influence both the template parsing phase and the runtime
- * settings. For both aspects (i.e., {@link Configurable} and {@link ParserConfiguration}) to take effect, you have
- * first pass this object to the {@link Template} constructor (this is where the {@link ParserConfiguration} interface
- * is used), and then you have to call {@link #configure(Template)} on the resulting {@link Template} object (this is
- * where the {@link Configurable} aspect is used).
- * 
- * <li>{@link #configure(Template)} only change the settings that weren't yet set on the {@link Template} (but are
- * inherited from the {@link Configuration}). This is primarily because if the template configures itself via the
- * {@code #ftl} header, those values should have precedence. A consequence of this is that if you want to configure
- * the same {@link Template} with multiple {@link TemplateConfigurer}-s, you either should merge them to a single one
- * before that (with {@link #merge(TemplateConfigurer)}), or you have to apply them in reverse order of their intended
- * precedence. 
- * </ul>
- * 
- * @see Template#Template(String, String, Reader, Configuration, ParserConfiguration, String)
- * 
- * @since 2.3.24
- */
-public final class TemplateConfigurer extends Configurable implements ParserConfiguration {
-
-    private boolean parentConfigurationSet;
-    private Integer tagSyntax;
-    private Integer namingConvention;
-    private Boolean whitespaceStripping;
-    private Boolean strictSyntaxMode;
-    private Integer autoEscapingPolicy;
-    private Boolean recognizeStandardFileExtensions;
-    private OutputFormat outputFormat;
-    private String encoding;
-
-    /**
-     * Creates a new instance. The parent will be {@link Configuration#getDefaultConfiguration()} initially, but it will
-     * be changed to the real parent {@link Configuration} when this object is added to the {@link Configuration}. (It's
-     * not allowed to add the same instance to multiple {@link Configuration}-s).
-     */
-    public TemplateConfigurer() {
-        super(Configuration.getDefaultConfiguration());
-    }
-
-    /**
-     * Same as {@link #setParentConfiguration(Configuration)}.
-     * 
-     * @throws IllegalArgumentException
-     *             if the argument is {@code null} or not a {@link Configuration}.
-     */
-    @Override
-    void setParent(Configurable cfg) {
-        NullArgumentException.check("cfg", cfg);
-        if (!(cfg instanceof Configuration)) {
-            throw new IllegalArgumentException("The parent of a TemplateConfigurer can only be a Configuration");
-        }
-        
-        if (parentConfigurationSet) {
-            if (getParent() != cfg) {
-                throw new IllegalStateException(
-                        "This TemplateConfigurer is already associated with a different Configuration instance.");
-            }
-            return;
-        }
-        
-        if (((Configuration) cfg).getIncompatibleImprovements().intValue() < _TemplateAPI.VERSION_INT_2_3_22
-                && hasAnyConfigurableSet()) {
-            throw new IllegalStateException(
-                    "This TemplateConfigurer can't be associated to a Configuration that has incompatibleImprovements "
-                    + "less than 2.3.22, because it changes non-parser settings.");
-        }
-        
-        super.setParent(cfg);
-        parentConfigurationSet = true;
-    }
-
-    /**
-     * Associates this instance with a {@link Configuration}; usually you don't call this, as it's called internally
-     * when this instance is added to a {@link Configuration}. This method can be called only once (except with the same
-     * {@link Configuration} parameter again, as that changes nothing anyway).
-     * 
-     * @throws IllegalStateException
-     *             If the parent configuration was already set to a different {@link Configuration} instance.
-     * @throws IllegalArgumentException
-     *             if the argument is {@code null}.
-     */
-    public void setParentConfiguration(Configuration cfg) {
-        setParent(cfg);
-    }
-
-    /**
-     * Returns the parent {@link Configuration}, or {@code null} if none was associated yet.
-     */
-    public Configuration getParentConfiguration() {
-        return parentConfigurationSet ? (Configuration) getParent() : null;
-    }
-    
-    /**
-     * Set all settings in this {@link TemplateConfigurer} that that were set in the parameter
-     * {@link TemplateConfigurer}, possibly overwriting the earlier value in this object. (A setting is said to be set
-     * in a {@link TemplateConfigurer} if it was explicitly set via a setter method, as opposed to be inherited.)
-     */
-    public void merge(TemplateConfigurer tc) {
-        if (tc.isAPIBuiltinEnabledSet()) {
-            setAPIBuiltinEnabled(tc.isAPIBuiltinEnabled());
-        }
-        if (tc.isArithmeticEngineSet()) {
-            setArithmeticEngine(tc.getArithmeticEngine());
-        }
-        if (tc.isAutoEscapingPolicySet()) {
-            setAutoEscapingPolicy(tc.getAutoEscapingPolicy());
-        }
-        if (tc.isAutoFlushSet()) {
-            setAutoFlush(tc.getAutoFlush());
-        }
-        if (tc.isBooleanFormatSet()) {
-            setBooleanFormat(tc.getBooleanFormat());
-        }
-        if (tc.isClassicCompatibleSet()) {
-            setClassicCompatibleAsInt(tc.getClassicCompatibleAsInt());
-        }
-        if (tc.isCustomDateFormatsSet()) {
-            setCustomDateFormats(mergeMaps(getCustomDateFormats(), tc.getCustomDateFormats()));
-        }
-        if (tc.isCustomNumberFormatsSet()) {
-            setCustomNumberFormats(mergeMaps(getCustomNumberFormats(), tc.getCustomNumberFormats()));
-        }
-        if (tc.isDateFormatSet()) {
-            setDateFormat(tc.getDateFormat());
-        }
-        if (tc.isDateTimeFormatSet()) {
-            setDateTimeFormat(tc.getDateTimeFormat());
-        }
-        if (tc.isEncodingSet()) {
-            setEncoding(tc.getEncoding());
-        }
-        if (tc.isLocaleSet()) {
-            setLocale(tc.getLocale());
-        }
-        if (tc.isLogTemplateExceptionsSet()) {
-            setLogTemplateExceptions(tc.getLogTemplateExceptions());
-        }
-        if (tc.isNamingConventionSet()) {
-            setNamingConvention(tc.getNamingConvention());
-        }
-        if (tc.isNewBuiltinClassResolverSet()) {
-            setNewBuiltinClassResolver(tc.getNewBuiltinClassResolver());
-        }
-        if (tc.isNumberFormatSet()) {
-            setNumberFormat(tc.getNumberFormat());
-        }
-        if (tc.isObjectWrapperSet()) {
-            setObjectWrapper(tc.getObjectWrapper());
-        }
-        if (tc.isOutputEncodingSet()) {
-            setOutputEncoding(tc.getOutputEncoding());
-        }
-        if (tc.isOutputFormatSet()) {
-            setOutputFormat(tc.getOutputFormat());
-        }
-        if (tc.isRecognizeStandardFileExtensionsSet()) {
-            setRecognizeStandardFileExtensions(tc.getRecognizeStandardFileExtensions());
-        }
-        if (tc.isShowErrorTipsSet()) {
-            setShowErrorTips(tc.getShowErrorTips());
-        }
-        if (tc.isSQLDateAndTimeTimeZoneSet()) {
-            setSQLDateAndTimeTimeZone(tc.getSQLDateAndTimeTimeZone());
-        }
-        if (tc.isStrictSyntaxModeSet()) {
-            setStrictSyntaxMode(tc.getStrictSyntaxMode());
-        }
-        if (tc.isTagSyntaxSet()) {
-            setTagSyntax(tc.getTagSyntax());
-        }
-        if (tc.isTemplateExceptionHandlerSet()) {
-            setTemplateExceptionHandler(tc.getTemplateExceptionHandler());
-        }
-        if (tc.isTimeFormatSet()) {
-            setTimeFormat(tc.getTimeFormat());
-        }
-        if (tc.isTimeZoneSet()) {
-            setTimeZone(tc.getTimeZone());
-        }
-        if (tc.isURLEscapingCharsetSet()) {
-            setURLEscapingCharset(tc.getURLEscapingCharset());
-        }
-        if (tc.isWhitespaceStrippingSet()) {
-            setWhitespaceStripping(tc.getWhitespaceStripping());
-        }
-        
-        tc.copyDirectCustomAttributes(this, true);
-    }
-
-    /**
-     * Sets the settings of the {@link Template} which are not yet set in the {@link Template} and are set in this
-     * {@link TemplateConfigurer}, leaves the other settings as is. A setting is said to be set in a
-     * {@link TemplateConfigurer} or {@link Template} if it was explicitly set via a setter method on that object, as
-     * opposed to be inherited from the {@link Configuration}.
-     * 
-     * <p>
-     * Note that the {@code encoding} setting of the {@link Template} counts as unset if it's {@code null},
-     * even if {@code null} was set via {@link Template#setEncoding(String)}.
-     *
-     * @throws IllegalStateException
-     *             If the parent configuration wasn't yet set.
-     */
-    public void configure(Template template) {
-        checkParentConfigurationSet();
-        Configuration cfg = getParentConfiguration();
-        if (template.getConfiguration() != cfg) {
-            // This is actually not a problem right now, but for future BC we enforce this.
-            throw new IllegalArgumentException(
-                    "The argument Template doesn't belong to the same Configuration as the TemplateConfigurer");
-        }
-
-        if (isAPIBuiltinEnabledSet() && !template.isAPIBuiltinEnabledSet()) {
-            template.setAPIBuiltinEnabled(isAPIBuiltinEnabled());
-        }
-        if (isArithmeticEngineSet() && !template.isArithmeticEngineSet()) {
-            template.setArithmeticEngine(getArithmeticEngine());
-        }
-        if (isAutoFlushSet() && !template.isAutoFlushSet()) {
-            template.setAutoFlush(getAutoFlush());
-        }
-        if (isBooleanFormatSet() && !template.isBooleanFormatSet()) {
-            template.setBooleanFormat(getBooleanFormat());
-        }
-        if (isClassicCompatibleSet() && !template.isClassicCompatibleSet()) {
-            template.setClassicCompatibleAsInt(getClassicCompatibleAsInt());
-        }
-        if (isCustomDateFormatsSet() && !template.isCustomDateFormatsSet()) {
-            template.setCustomDateFormats(getCustomDateFormats());
-        }
-        if (isCustomNumberFormatsSet() && !template.isCustomNumberFormatsSet()) {
-            template.setCustomNumberFormats(getCustomNumberFormats());
-        }
-        if (isDateFormatSet() && !template.isDateFormatSet()) {
-            template.setDateFormat(getDateFormat());
-        }
-        if (isDateTimeFormatSet() && !template.isDateTimeFormatSet()) {
-            template.setDateTimeFormat(getDateTimeFormat());
-        }
-        if (isEncodingSet() && template.getEncoding() == null) {
-            template.setEncoding(getEncoding());
-        }
-        if (isLocaleSet() && !template.isLocaleSet()) {
-            template.setLocale(getLocale());
-        }
-        if (isLogTemplateExceptionsSet() && !template.isLogTemplateExceptionsSet()) {
-            template.setLogTemplateExceptions(getLogTemplateExceptions());
-        }
-        if (isNewBuiltinClassResolverSet() && !template.isNewBuiltinClassResolverSet()) {
-            template.setNewBuiltinClassResolver(getNewBuiltinClassResolver());
-        }
-        if (isNumberFormatSet() && !template.isNumberFormatSet()) {
-            template.setNumberFormat(getNumberFormat());
-        }
-        if (isObjectWrapperSet() && !template.isObjectWrapperSet()) {
-            template.setObjectWrapper(getObjectWrapper());
-        }
-        if (isOutputEncodingSet() && !template.isOutputEncodingSet()) {
-            template.setOutputEncoding(getOutputEncoding());
-        }
-        if (isShowErrorTipsSet() && !template.isShowErrorTipsSet()) {
-            template.setShowErrorTips(getShowErrorTips());
-        }
-        if (isSQLDateAndTimeTimeZoneSet() && !template.isSQLDateAndTimeTimeZoneSet()) {
-            template.setSQLDateAndTimeTimeZone(getSQLDateAndTimeTimeZone());
-        }
-        if (isTemplateExceptionHandlerSet() && !template.isTemplateExceptionHandlerSet()) {
-            template.setTemplateExceptionHandler(getTemplateExceptionHandler());
-        }
-        if (isTimeFormatSet() && !template.isTimeFormatSet()) {
-            template.setTimeFormat(getTimeFormat());
-        }
-        if (isTimeZoneSet() && !template.isTimeZoneSet()) {
-            template.setTimeZone(getTimeZone());
-        }
-        if (isURLEscapingCharsetSet() && !template.isURLEscapingCharsetSet()) {
-            template.setURLEscapingCharset(getURLEscapingCharset());
-        }
-        
-        copyDirectCustomAttributes(template, false);
-    }
-
-    /**
-     * See {@link Configuration#setTagSyntax(int)}.
-     */
-    public void setTagSyntax(int tagSyntax) {
-        _TemplateAPI.valideTagSyntaxValue(tagSyntax);
-        this.tagSyntax = Integer.valueOf(tagSyntax);
-    }
-
-    /**
-     * The getter pair of {@link #setTagSyntax(int)}.
-     */
-    public int getTagSyntax() {
-        return tagSyntax != null ? tagSyntax.intValue() : getParentConfiguration().getTagSyntax();
-    }
-
-    /**
-     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
-     */
-    public boolean isTagSyntaxSet() {
-        return tagSyntax != null;
-    }
-
-    /**
-     * See {@link Configuration#setNamingConvention(int)}.
-     */
-    public void setNamingConvention(int namingConvention) {
-        _TemplateAPI.validateNamingConventionValue(namingConvention);
-        this.namingConvention = Integer.valueOf(namingConvention);
-    }
-
-    /**
-     * The getter pair of {@link #setNamingConvention(int)}.
-     */
-    public int getNamingConvention() {
-        return namingConvention != null ? namingConvention.intValue() : getParentConfiguration().getNamingConvention();
-    }
-
-    /**
-     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
-     */
-    public boolean isNamingConventionSet() {
-        return namingConvention != null;
-    }
-
-    /**
-     * See {@link Configuration#setWhitespaceStripping(boolean)}.
-     */
-    public void setWhitespaceStripping(boolean whitespaceStripping) {
-        this.whitespaceStripping = Boolean.valueOf(whitespaceStripping);
-    }
-
-    /**
-     * The getter pair of {@link #getWhitespaceStripping()}.
-     */
-    public boolean getWhitespaceStripping() {
-        return whitespaceStripping != null ? whitespaceStripping.booleanValue()
-                : getParentConfiguration().getWhitespaceStripping();
-    }
-
-    /**
-     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
-     */
-    public boolean isWhitespaceStrippingSet() {
-        return whitespaceStripping != null;
-    }
-
-    /**
-     * Sets the output format of the template; see {@link Configuration#setAutoEscapingPolicy(int)} for more.
-     */
-    public void setAutoEscapingPolicy(int autoEscapingPolicy) {
-        _TemplateAPI.validateAutoEscapingPolicyValue(autoEscapingPolicy);
-        this.autoEscapingPolicy = Integer.valueOf(autoEscapingPolicy);
-    }
-
-    /**
-     * The getter pair of {@link #setAutoEscapingPolicy(int)}.
-     */
-    public int getAutoEscapingPolicy() {
-        return autoEscapingPolicy != null ? autoEscapingPolicy.intValue()
-                : getParentConfiguration().getAutoEscapingPolicy();
-    }
-
-    /**
-     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
-     */
-    public boolean isAutoEscapingPolicySet() {
-        return autoEscapingPolicy != null;
-    }
-
-    /**
-     * Sets the output format of the template; see {@link Configuration#setOutputFormat(OutputFormat)} for more.
-     */
-    public void setOutputFormat(OutputFormat outputFormat) {
-        NullArgumentException.check("outputFormat", outputFormat);
-        this.outputFormat = outputFormat;
-    }
-
-    /**
-     * The getter pair of {@link #setOutputFormat(OutputFormat)}.
-     */
-    public OutputFormat getOutputFormat() {
-        return outputFormat != null ? outputFormat : getParentConfiguration().getOutputFormat();
-    }
-
-    /**
-     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
-     */
-    public boolean isOutputFormatSet() {
-        return outputFormat != null;
-    }
-    
-    /**
-     * See {@link Configuration#setRecognizeStandardFileExtensions(boolean)}. 
-     */
-    public void setRecognizeStandardFileExtensions(boolean recognizeStandardFileExtensions) {
-        this.recognizeStandardFileExtensions = Boolean.valueOf(recognizeStandardFileExtensions);
-    }
-
-    /**
-     * Getter pair of {@link #setRecognizeStandardFileExtensions(boolean)}.
-     */
-    public boolean getRecognizeStandardFileExtensions() {
-        return recognizeStandardFileExtensions != null ? recognizeStandardFileExtensions.booleanValue()
-                : getParentConfiguration().getRecognizeStandardFileExtensions();
-    }
-    
-    /**
-     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
-     */
-    public boolean isRecognizeStandardFileExtensionsSet() {
-        return recognizeStandardFileExtensions != null;
-    }
-    
-    /**
-     * See {@link Configuration#setStrictSyntaxMode(boolean)}.
-     */
-    public void setStrictSyntaxMode(boolean strictSyntaxMode) {
-        this.strictSyntaxMode = Boolean.valueOf(strictSyntaxMode);
-    }
-
-    /**
-     * The getter pair of {@link #setStrictSyntaxMode(boolean)}.
-     */
-    public boolean getStrictSyntaxMode() {
-        return strictSyntaxMode != null ? strictSyntaxMode.booleanValue()
-                : getParentConfiguration().getStrictSyntaxMode();
-    }
-    
-    /**
-     * Tells if this setting is set directly in this object or its value is coming from the {@link #getParent() parent}.
-     */
-    public boolean isStrictSyntaxModeSet() {
-        return strictSyntaxMode != null;
-    }
-
-    @Override
-    public void setStrictBeanModels(boolean strict) {
-        throw new UnsupportedOperationException(
-                "Setting strictBeanModels on " + TemplateConfigurer.class.getSimpleName() + " level isn't supported.");
-    }
-
-    public String getEncoding() {
-        return encoding != null ? encoding : getParentConfiguration().getDefaultEncoding();
-    }
-
-    /**
-     * When the standard template loading/caching mechanism is used, this forces the charset used for reading the
-     * template "file", overriding everything but the encoding coming from the {@code #ftl} header. This setting
-     * overrides the locale-specific encodings set via {@link Configuration#setEncoding(java.util.Locale, String)}. It
-     * also overrides the {@code encoding} parameter of {@link Configuration#getTemplate(String, String)} (and of its
-     * overloads) and the {@code encoding} parameter of the {@code #include} directive. This works like that because
-     * specifying the encoding where you are requesting the template is error prone and deprecated.
-     * 
-     * <p>
-     * If you are developing your own template loading/caching mechanism instead of the standard one, note that the
-     * above behavior is not guaranteed by this class alone; you have to ensure it. Also, read the note on
-     * {@code encoding} in the documentation of {@link #configure(Template)}.
-     */
-    public void setEncoding(String encoding) {
-        NullArgumentException.check("encoding", encoding);
-        this.encoding = encoding;
-    }
-
-    public boolean isEncodingSet() {
-        return encoding != null;
-    }
-    
-    /**
-     * Returns {@link Configuration#getIncompatibleImprovements()} from the parent {@link Configuration}. This mostly
-     * just exist to satisfy the {@link ParserConfiguration} interface.
-     * 
-     * @throws IllegalStateException
-     *             If the parent configuration wasn't yet set.
-     */
-    public Version getIncompatibleImprovements() {
-        checkParentConfigurationSet();
-        return getParentConfiguration().getIncompatibleImprovements();
-    }
-
-    private void checkParentConfigurationSet() {
-        if (!parentConfigurationSet) {
-            throw new IllegalStateException("The TemplateConfigurer wasn't associated with a Configuration yet.");
-        }
-    }
-
-    private boolean hasAnyConfigurableSet() {
-        return
-                isAPIBuiltinEnabledSet()
-                || isArithmeticEngineSet()
-                || isAutoFlushSet()
-                || isBooleanFormatSet()
-                || isClassicCompatibleSet()
-                || isCustomDateFormatsSet()
-                || isCustomNumberFormatsSet()
-                || isDateFormatSet()
-                || isDateTimeFormatSet()
-                || isLocaleSet()
-                || isLogTemplateExceptionsSet()
-                || isNewBuiltinClassResolverSet()
-                || isNumberFormatSet()
-                || isObjectWrapperSet()
-                || isOutputEncodingSet()
-                || isShowErrorTipsSet()
-                || isSQLDateAndTimeTimeZoneSet()
-                || isTemplateExceptionHandlerSet()
-                || isTimeFormatSet()
-                || isTimeZoneSet()
-                || isURLEscapingCharsetSet();
-    }
-    
-    private Map mergeMaps(Map m1, Map m2) {
-        if (m1 == null) return m2;
-        if (m2 == null) return m1;
-        if (m1.isEmpty()) return m2;
-        if (m2.isEmpty()) return m1;
-        
-        LinkedHashMap mergedM = new LinkedHashMap(m1);
-        mergedM.putAll(m2);
-        return mergedM;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/core/UndefinedOutputFormat.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/UndefinedOutputFormat.java b/src/main/java/freemarker/core/UndefinedOutputFormat.java
index d4cbdd1..4dce258 100644
--- a/src/main/java/freemarker/core/UndefinedOutputFormat.java
+++ b/src/main/java/freemarker/core/UndefinedOutputFormat.java
@@ -23,9 +23,9 @@ import freemarker.template.Configuration;
 /**
  * Represents the output format used when the template output format is undecided. This is the default output format if
  * FreeMarker can't select anything more specific (see
- * {@link Configuration#setTemplateConfigurers(freemarker.cache.TemplateConfigurerFactory)}). This format doesn't
- * support auto-escaping ({@link Configuration#setAutoEscapingPolicy(int)}). It will print {@link TemplateMarkupOutputModel}-s
- * as is (doesn't try to convert them).
+ * {@link Configuration#setTemplateConfigurations(freemarker.cache.TemplateConfigurationFactory)}). This format doesn't
+ * support auto-escaping ({@link Configuration#setAutoEscapingPolicy(int)}). It will print
+ * {@link TemplateMarkupOutputModel}-s as is (doesn't try to convert them).
  * 
  * @see PlainTextOutputFormat
  * 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/core/_ObjectBuilderSettingEvaluator.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/_ObjectBuilderSettingEvaluator.java b/src/main/java/freemarker/core/_ObjectBuilderSettingEvaluator.java
index efcd3cc..a1e1c22 100644
--- a/src/main/java/freemarker/core/_ObjectBuilderSettingEvaluator.java
+++ b/src/main/java/freemarker/core/_ObjectBuilderSettingEvaluator.java
@@ -36,11 +36,11 @@ import java.util.Map;
 import java.util.Properties;
 
 import freemarker.cache.AndMatcher;
-import freemarker.cache.ConditionalTemplateConfigurerFactory;
+import freemarker.cache.ConditionalTemplateConfigurationFactory;
 import freemarker.cache.FileExtensionMatcher;
 import freemarker.cache.FileNameGlobMatcher;
-import freemarker.cache.FirstMatchTemplateConfigurerFactory;
-import freemarker.cache.MergingTemplateConfigurerFactory;
+import freemarker.cache.FirstMatchTemplateConfigurationFactory;
+import freemarker.cache.MergingTemplateConfigurationFactory;
 import freemarker.cache.NotMatcher;
 import freemarker.cache.OrMatcher;
 import freemarker.cache.PathGlobMatcher;
@@ -661,7 +661,7 @@ public class _ObjectBuilderSettingEvaluator {
             addWithSimpleName(SHORTHANDS, BeansWrapper.class);
             addWithSimpleName(SHORTHANDS, SimpleObjectWrapper.class);
 
-            addWithSimpleName(SHORTHANDS, TemplateConfigurer.class);
+            addWithSimpleName(SHORTHANDS, TemplateConfiguration.class);
             
             addWithSimpleName(SHORTHANDS, PathGlobMatcher.class);
             addWithSimpleName(SHORTHANDS, FileNameGlobMatcher.class);
@@ -671,9 +671,9 @@ public class _ObjectBuilderSettingEvaluator {
             addWithSimpleName(SHORTHANDS, OrMatcher.class);
             addWithSimpleName(SHORTHANDS, NotMatcher.class);
             
-            addWithSimpleName(SHORTHANDS, ConditionalTemplateConfigurerFactory.class);
-            addWithSimpleName(SHORTHANDS, MergingTemplateConfigurerFactory.class);
-            addWithSimpleName(SHORTHANDS, FirstMatchTemplateConfigurerFactory.class);
+            addWithSimpleName(SHORTHANDS, ConditionalTemplateConfigurationFactory.class);
+            addWithSimpleName(SHORTHANDS, MergingTemplateConfigurationFactory.class);
+            addWithSimpleName(SHORTHANDS, FirstMatchTemplateConfigurationFactory.class);
 
             addWithSimpleName(SHORTHANDS, HTMLOutputFormat.class);
             addWithSimpleName(SHORTHANDS, XMLOutputFormat.class);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java b/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java
index 6ad1040..498466e 100644
--- a/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java
+++ b/src/main/java/freemarker/ext/servlet/FreemarkerServlet.java
@@ -199,7 +199,7 @@ import freemarker.template.utility.StringUtil;
  * {@link HttpServletResponse#setCharacterEncoding(String)} method. Note that the charset of a template usually comes
  * from {@link Configuration#getDefaultEncoding()} (i.e., from the {@code default_encoding} FreeMarker setting),
  * occasionally from {@link Configuration#getEncoding(Locale)} (when FreeMarker was configured to use different charsets
- * depending on the locale) or even more rarely from {@link Configuration#getTemplateConfigurers()} (when FreeMarker was
+ * depending on the locale) or even more rarely from {@link Configuration#getTemplateConfigurations()} (when FreeMarker was
  * configured to use a specific charset for certain templates).
  * <li>{@value #INIT_PARAM_VALUE_FROM_TEMPLATE}: This should be used in most applications, but it's not the default for
  * backward compatibility. It reads the {@link Configurable#getOutputEncoding()} setting of the template (note that the

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/template/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java
index c04f37a..0e67121 100644
--- a/src/main/java/freemarker/template/Configuration.java
+++ b/src/main/java/freemarker/template/Configuration.java
@@ -51,7 +51,7 @@ import freemarker.cache.MultiTemplateLoader;
 import freemarker.cache.SoftCacheStorage;
 import freemarker.cache.TemplateCache;
 import freemarker.cache.TemplateCache.MaybeMissingTemplate;
-import freemarker.cache.TemplateConfigurerFactory;
+import freemarker.cache.TemplateConfigurationFactory;
 import freemarker.cache.TemplateLoader;
 import freemarker.cache.TemplateLookupContext;
 import freemarker.cache.TemplateLookupStrategy;
@@ -68,7 +68,7 @@ import freemarker.core.ParseException;
 import freemarker.core.ParserConfiguration;
 import freemarker.core.PlainTextOutputFormat;
 import freemarker.core.RTFOutputFormat;
-import freemarker.core.TemplateConfigurer;
+import freemarker.core.TemplateConfiguration;
 import freemarker.core.TemplateMarkupOutputModel;
 import freemarker.core.UndefinedOutputFormat;
 import freemarker.core.UnregisteredOutputFormatException;
@@ -268,11 +268,11 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
     public static final String TEMPLATE_NAME_FORMAT_KEY = TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE;
 
     /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.24 */
-    public static final String TEMPLATE_CONFIGURERS_KEY_SNAKE_CASE = "template_configurers";
+    public static final String TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE = "template_configurations";
     /** Modern, camel case ({@code likeThis}) variation of the setting name. @since 2.3.24 */
-    public static final String TEMPLATE_CONFIGURERS_KEY_CAMEL_CASE = "templateConfigurers";
+    public static final String TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE = "templateConfigurations";
     /** Alias to the {@code ..._SNAKE_CASE} variation. @since 2.3.24 */
-    public static final String TEMPLATE_CONFIGURERS_KEY = TEMPLATE_CONFIGURERS_KEY_SNAKE_CASE;
+    public static final String TEMPLATE_CONFIGURATIONS_KEY = TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE;
     
     /** Legacy, snake case ({@code like_this}) variation of the setting name. @since 2.3.23 */
     public static final String INCOMPATIBLE_IMPROVEMENTS_KEY_SNAKE_CASE = "incompatible_improvements";
@@ -303,7 +303,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
         REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_SNAKE_CASE,
         STRICT_SYNTAX_KEY_SNAKE_CASE,
         TAG_SYNTAX_KEY_SNAKE_CASE,
-        TEMPLATE_CONFIGURERS_KEY_SNAKE_CASE,
+        TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE,
         TEMPLATE_LOADER_KEY_SNAKE_CASE,
         TEMPLATE_LOOKUP_STRATEGY_KEY_SNAKE_CASE,
         TEMPLATE_NAME_FORMAT_KEY_SNAKE_CASE,
@@ -326,7 +326,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
         REGISTERED_CUSTOM_OUTPUT_FORMATS_KEY_CAMEL_CASE,
         STRICT_SYNTAX_KEY_CAMEL_CASE,
         TAG_SYNTAX_KEY_CAMEL_CASE,
-        TEMPLATE_CONFIGURERS_KEY_CAMEL_CASE,
+        TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE,
         TEMPLATE_LOADER_KEY_CAMEL_CASE,
         TEMPLATE_LOOKUP_STRATEGY_KEY_CAMEL_CASE,
         TEMPLATE_NAME_FORMAT_KEY_CAMEL_CASE,
@@ -815,10 +815,10 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
     private void recreateTemplateCacheWith(
             TemplateLoader loader, CacheStorage storage,
             TemplateLookupStrategy templateLookupStrategy, TemplateNameFormat templateNameFormat,
-            TemplateConfigurerFactory templateConfigurers) {
+            TemplateConfigurationFactory templateConfigurations) {
         TemplateCache oldCache = cache;
         cache = new TemplateCache(
-                loader, storage, templateLookupStrategy, templateNameFormat, templateConfigurers, this);
+                loader, storage, templateLookupStrategy, templateNameFormat, templateConfigurations, this);
         cache.clear(); // for fully BC behavior
         cache.setDelay(oldCache.getDelay());
         cache.setLocalizedLookup(localizedLookup);
@@ -827,7 +827,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
     private void recreateTemplateCache() {
         recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),
                 cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),
-                getTemplateConfigurers());
+                getTemplateConfigurations());
     }
     
     private TemplateLoader getDefaultTemplateLoader() {
@@ -932,7 +932,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
             copy.recreateTemplateCacheWith(
                     cache.getTemplateLoader(), cache.getCacheStorage(),
                     cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),
-                    cache.getTemplateConfigurers());
+                    cache.getTemplateConfigurations());
             return copy;
         } catch (CloneNotSupportedException e) {
             throw new BugException("Cloning failed", e);
@@ -1122,7 +1122,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
             if (cache.getTemplateLoader() != templateLoader) {
                 recreateTemplateCacheWith(templateLoader, cache.getCacheStorage(),
                         cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),
-                        cache.getTemplateConfigurers());
+                        cache.getTemplateConfigurations());
             }
             templateLoaderExplicitlySet = true;
         }
@@ -1171,7 +1171,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
         if (cache.getTemplateLookupStrategy() != templateLookupStrategy) {
             recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),
                     templateLookupStrategy, cache.getTemplateNameFormat(),
-                    cache.getTemplateConfigurers());
+                    cache.getTemplateConfigurations());
         }
         templateLookupStrategyExplicitlySet = true;
     }
@@ -1220,7 +1220,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
         if (cache.getTemplateNameFormat() != templateNameFormat) {
             recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),
                     cache.getTemplateLookupStrategy(), templateNameFormat,
-                    cache.getTemplateConfigurers());
+                    cache.getTemplateConfigurations());
         }
         templateNameFormatExplicitlySet = true;
     }
@@ -1259,38 +1259,38 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
     }
     
     /**
-     * Sets a {@link TemplateConfigurerFactory} that will configure individual templates where their settings differ
+     * Sets a {@link TemplateConfigurationFactory} that will configure individual templates where their settings differ
      * from those coming from the common {@link Configuration} object. A typical use case for that is specifying the
-     * {@link TemplateConfigurer#setOutputFormat(OutputFormat) outputFormat} for templates based on their file
+     * {@link TemplateConfiguration#setOutputFormat(OutputFormat) outputFormat} for templates based on their file
      * extension or parent directory.
      * 
      * <p>
      * Note that the settings suggested by standard file extensions are stronger than that you set here. See
      * {@link #setRecognizeStandardFileExtensions(boolean)} for more information about standard file extensions.
      * 
-     * <p>See "Template configurers" in the FreeMarker Manual for examples.
+     * <p>See "Template configurations" in the FreeMarker Manual for examples.
      * 
      * @since 2.3.24
      */
-    public void setTemplateConfigurers(TemplateConfigurerFactory templateConfigurers) {
-        if (cache.getTemplateConfigurers() != templateConfigurers) {
-            if (templateConfigurers != null) {
-                templateConfigurers.setConfiguration(this);
+    public void setTemplateConfigurations(TemplateConfigurationFactory templateConfigurations) {
+        if (cache.getTemplateConfigurations() != templateConfigurations) {
+            if (templateConfigurations != null) {
+                templateConfigurations.setConfiguration(this);
             }
             recreateTemplateCacheWith(cache.getTemplateLoader(), cache.getCacheStorage(),
                     cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),
-                    templateConfigurers);
+                    templateConfigurations);
         }
     }
     
     /**
-     * The getter pair of {@link #setTemplateConfigurers(TemplateConfigurerFactory)}.
+     * The getter pair of {@link #setTemplateConfigurations(TemplateConfigurationFactory)}.
      */
-    public TemplateConfigurerFactory getTemplateConfigurers() {
+    public TemplateConfigurationFactory getTemplateConfigurations() {
         if (cache == null) {
             return null;
         }
-        return cache.getTemplateConfigurers();
+        return cache.getTemplateConfigurations();
     }
 
     /**
@@ -1311,7 +1311,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
             if (getCacheStorage() != cacheStorage) {
                 recreateTemplateCacheWith(cache.getTemplateLoader(), cacheStorage,
                         cache.getTemplateLookupStrategy(), cache.getTemplateNameFormat(),
-                        cache.getTemplateConfigurers());
+                        cache.getTemplateConfigurations());
             }
             cacheStorageExplicitlySet = true;
         }
@@ -1740,7 +1740,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
      * Auto-escaping has significance when a value is printed with <code>${...}</code> (or <code>#{...}</code>). If
      * auto-escaping is on, FreeMarker will assume that the value is plain text (as opposed to markup or some kind of
      * rich text), so it will escape it according the current output format (see {@link #setOutputFormat(OutputFormat)}
-     * and {@link TemplateConfigurer#setOutputFormat(OutputFormat)}). If auto-escaping is off, FreeMarker will assume
+     * and {@link TemplateConfiguration#setOutputFormat(OutputFormat)}). If auto-escaping is off, FreeMarker will assume
      * that the string value is already in the output format, so it prints it as is to the output.
      *
      * <p>Further notes on auto-escaping:
@@ -1759,16 +1759,16 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
      * </ul>
      * 
      * <p>Note that what you set here is just a default, which can be overridden for individual templates via
-     * {@link #setTemplateConfigurers(TemplateConfigurerFactory)}. This setting is also overridden by the standard file
+     * {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. This setting is also overridden by the standard file
      * extensions; see them at {@link #setRecognizeStandardFileExtensions(boolean)}.
      * 
      * @param autoEscapingPolicy
      *          One of the {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY},
      *          {@link #ENABLE_IF_SUPPORTED_AUTO_ESCAPING_POLICY}, and {@link #DISABLE_AUTO_ESCAPING_POLICY} constants.  
      * 
-     * @see TemplateConfigurer#setAutoEscapingPolicy(int)
+     * @see TemplateConfiguration#setAutoEscapingPolicy(int)
      * @see Configuration#setOutputFormat(OutputFormat)
-     * @see TemplateConfigurer#setOutputFormat(OutputFormat)
+     * @see TemplateConfiguration#setOutputFormat(OutputFormat)
      * 
      * @since 2.3.24
      */
@@ -1794,7 +1794,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
     /**
      * Sets the (default) output format. Usually, you leave this on its default, which is
      * {@link UndefinedOutputFormat#INSTANCE}, and then override it for individual templates based on their name (like
-     * based on their "file" extension) with {@link #setTemplateConfigurers(TemplateConfigurerFactory)}. This setting is
+     * based on their "file" extension) with {@link #setTemplateConfigurations(TemplateConfigurationFactory)}. This setting is
      * also overridden by the standard file extensions; see them at
      * {@link #setRecognizeStandardFileExtensions(boolean)}.
      * 
@@ -1803,7 +1803,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
      * also used by the embedding application to set the HTTP response MIME type, etc.
      * 
      * @see #setRegisteredCustomOutputFormats(Collection)
-     * @see #setTemplateConfigurers(TemplateConfigurerFactory)
+     * @see #setTemplateConfigurations(TemplateConfigurationFactory)
      * @see #setRecognizeStandardFileExtensions(boolean)
      * @see #setAutoEscapingPolicy(int)
      * 
@@ -2018,26 +2018,26 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
      * Sets if the "file" extension part of the source name ({@link Template#getSourceName()}) will influence certain
      * parsing settings. For backward compatibility, it defaults to {@code false} if
      * {@link #getIncompatibleImprovements()} is less than 2.3.24. Starting from {@code incompatibleImprovements}
-     * 2.3.24, defaults to {@code true}, so the following standard file extensions take their effect:
+     * 2.3.24, it defaults to {@code true}, so the following standard file extensions take their effect:
      * 
      * <ul>
-     *   <li>{@code ftlh}: Sets {@link TemplateConfigurer#setOutputFormat(OutputFormat) outputFormat} to {@code "HTML"}
-     *       (i.e., {@link HTMLOutputFormat#INSTANCE} unless the {@code "HTML"} name is overridden by
+     *   <li>{@code ftlh}: Sets {@link TemplateConfiguration#setOutputFormat(OutputFormat) outputFormat} to
+     *       {@code "HTML"} (i.e., {@link HTMLOutputFormat#INSTANCE}, unless the {@code "HTML"} name is overridden by
      *       {@link #setRegisteredCustomOutputFormats(Collection)}) and
-     *       {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}
-     *       {@link TemplateConfigurer#setAutoEscapingPolicy(int) autoEscapingPolicy}.
-     *   <li>{@code ftlx}: Sets {@link TemplateConfigurer#setOutputFormat(OutputFormat) outputFormat} to {@code "XML"}
-     *       (i.e., {@link XMLOutputFormat#INSTANCE} unless the {@code "XML"} name is overridden by
+     *       {@link TemplateConfiguration#setAutoEscapingPolicy(int) autoEscapingPolicy} to
+     *       {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}.
+     *   <li>{@code ftlx}: Sets {@link TemplateConfiguration#setOutputFormat(OutputFormat) outputFormat} to
+     *       {@code "XML"} (i.e., {@link XMLOutputFormat#INSTANCE}, unless the {@code "XML"} name is overridden by
      *       {@link #setRegisteredCustomOutputFormats(Collection)}) and
-     *       {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}
-     *       {@link TemplateConfigurer#setAutoEscapingPolicy(int) autoEscapingPolicy}.
+     *       {@link TemplateConfiguration#setAutoEscapingPolicy(int) autoEscapingPolicy} to
+     *       {@link #ENABLE_IF_DEFAULT_AUTO_ESCAPING_POLICY}.
      * </ul>
      * 
      * <p>These file extensions are not case sensitive. The file extension is the part after the last dot in the source
      * name. If the source name contains no dot, then it has no file extension.
      * 
-     * <p>The settings activated by these file extensions override the settings values dictated by
-     * {@link #setTemplateConfigurers(TemplateConfigurerFactory)}.
+     * <p>The settings activated by these file extensions override the setting values dictated by
+     * {@link #setTemplateConfigurations(TemplateConfigurationFactory)}.
      */
     public void setRecognizeStandardFileExtensions(boolean recognizeStandardFileExtensions) {
         boolean prevEffectiveValue = getRecognizeStandardFileExtensions();
@@ -2274,7 +2274,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
      *
      * @param locale
      *            The requested locale of the template. This is what {@link Template#getLocale()} on the resulting
-     *            {@link Template} will return (unless it's overridden via {@link #getTemplateConfigurers()}). This
+     *            {@link Template} will return (unless it's overridden via {@link #getTemplateConfigurations()}). This
      *            parameter can be {@code null} since 2.3.22, in which case it defaults to
      *            {@link Configuration#getLocale()} (note that {@link Template#getLocale()} will give the default value,
      *            not {@code null}). This parameter also drives localized template lookup. Assuming that you have
@@ -2284,7 +2284,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
      *            retrieve {@code myTemplate_en_US.html}, then {@code myTemplate.en.ftl}, and finally
      *            {@code myTemplate.ftl}. Note that that the template's locale will be {@code en_US} even if it only
      *            finds {@code myTemplate.ftl}. Note that when the {@code locale} setting is overridden with a
-     *            {@link TemplateConfigurer} provided by {@link #getTemplateConfigurers()}, that overrides the
+     *            {@link TemplateConfiguration} provided by {@link #getTemplateConfigurations()}, that overrides the
      *            value specified here, but only after the localized lookup, that is, it modifies the template
      *            found by the localized lookup.
      * 
@@ -2305,7 +2305,7 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
      *            instead). Why is this deprecated: It doesn't make sense to get the <em>same</em> template with
      *            different encodings, hence, it's error prone to specify the encoding where you get the template.
      *            Instead, if you have template "files" with different charsets, you should use
-     *            {@link #setTemplateConfigurers(TemplateConfigurerFactory)}, where you can associate encodings to
+     *            {@link #setTemplateConfigurations(TemplateConfigurationFactory)}, where you can associate encodings to
      *            individual templates based on their names (like which "directory" are they in, what's their file
      *            extension, etc.). The encoding associated with the templates that way overrides the encoding that you
      *            specify here.
@@ -2900,13 +2900,13 @@ public class Configuration extends Configurable implements Cloneable, ParserConf
                 } else {
                     throw invalidSettingValueException(name, value);
                 }
-            } else if (TEMPLATE_CONFIGURERS_KEY_SNAKE_CASE.equals(name)
-                    || TEMPLATE_CONFIGURERS_KEY_CAMEL_CASE.equals(name)) {
+            } else if (TEMPLATE_CONFIGURATIONS_KEY_SNAKE_CASE.equals(name)
+                    || TEMPLATE_CONFIGURATIONS_KEY_CAMEL_CASE.equals(name)) {
                 if (value.equals(NULL)) {
-                    setTemplateConfigurers(null);
+                    setTemplateConfigurations(null);
                 } else {
-                    setTemplateConfigurers((TemplateConfigurerFactory) _ObjectBuilderSettingEvaluator.eval(
-                            value, TemplateConfigurerFactory.class, false, _SettingEvaluationEnvironment.getCurrent()));
+                    setTemplateConfigurations((TemplateConfigurationFactory) _ObjectBuilderSettingEvaluator.eval(
+                            value, TemplateConfigurationFactory.class, false, _SettingEvaluationEnvironment.getCurrent()));
                 }
             } else {
                 unknown = true;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/main/java/freemarker/template/Template.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/Template.java b/src/main/java/freemarker/template/Template.java
index 0530fe2..f785768 100644
--- a/src/main/java/freemarker/template/Template.java
+++ b/src/main/java/freemarker/template/Template.java
@@ -48,7 +48,7 @@ import freemarker.core.Macro;
 import freemarker.core.OutputFormat;
 import freemarker.core.ParseException;
 import freemarker.core.ParserConfiguration;
-import freemarker.core.TemplateConfigurer;
+import freemarker.core.TemplateConfiguration;
 import freemarker.core.TemplateElement;
 import freemarker.core.TextBlock;
 import freemarker.core.TokenMgrError;
@@ -73,7 +73,7 @@ import freemarker.debug.impl.DebuggerService;
  * changing FreeMarker settings. Those must not be used while the template is being processed, or if the template object
  * is already accessible from multiple threads. If some templates need different settings that those coming from the
  * shared {@link Configuration}, and you are using {@link Configuration#getTemplate(String)} (or its overloads), then
- * see {@link Configuration#setTemplateConfigurers(freemarker.cache.TemplateConfigurerFactory)}.
+ * see {@link Configuration#setTemplateConfigurations(freemarker.cache.TemplateConfigurationFactory)}.
  */
 public class Template extends Configurable {
     public static final String DEFAULT_NAMESPACE_PREFIX = "D";
@@ -207,25 +207,25 @@ public class Template extends Configurable {
    
     /**
      * Same as {@link #Template(String, String, Reader, Configuration, String)}, but also specifies a
-     * {@link TemplateConfigurer}. This is mostly meant to be used by FreeMarker internally, but advanced users might
+     * {@link TemplateConfiguration}. This is mostly meant to be used by FreeMarker internally, but advanced users might
      * still find this useful.
      * 
      * @param customParserConfiguration
      *            Overrides the parsing related configuration settings of the {@link Configuration} parameter; can be
      *            {@code null}. This is useful as the {@link Configuration} is normally a singleton shared by all
-     *            templates, and so it's not good for specifying template-specific settings. (While
-     *            {@link Template} itself has methods to specify settings just for that template, those don't influence
-     *            the parsing, and you only have opportunity to call them after the parsing anyway.) This objects is
-     *            often a {@link TemplateConfigurer} whose parent is the {@link Configuration} parameter, and then it
+     *            templates, and so it's not good for specifying template-specific settings. (While {@link Template}
+     *            itself has methods to specify settings just for that template, those don't influence the parsing, and
+     *            you only have opportunity to call them after the parsing anyway.) This objects is often a
+     *            {@link TemplateConfiguration} whose parent is the {@link Configuration} parameter, and then it
      *            practically just overrides some of the parser settings, as the others are inherited from the
-     *            {@link Configuration}. Note that if this is a {@link TemplateConfigurer}, you will also want to call
-     *            {@link TemplateConfigurer#configure(Template)} on the resulting {@link Template} so that
+     *            {@link Configuration}. Note that if this is a {@link TemplateConfiguration}, you will also want to
+     *            call {@link TemplateConfiguration#apply(Template)} on the resulting {@link Template} so that
      *            {@link Configurable} settings will be set too, because this constructor only uses it as a
-     *            {@link ParserConfiguration}.  
+     *            {@link ParserConfiguration}.
      * @param encoding
      *            Same as in {@link #Template(String, String, Reader, Configuration, String)}. When it's non-{@code
-     *            null}, it overrides the value coming from the {@code TemplateConfigurer#getEncoding()} method of the
-     *            {@code templateConfigurer} parameter.
+     *            null}, it overrides the value coming from the {@link TemplateConfiguration#getEncoding()} method of
+     *            the {@code templateConfiguration} parameter.
      * 
      * @since 2.3.24
      */
@@ -572,7 +572,7 @@ public class Template extends Configurable {
     
     /**
      * Returns the {@link ParserConfiguration} that was used for parsing this template. This is most often the same
-     * object as {@link #getConfiguration()}, but sometimes it's a {@link TemplateConfigurer}, or something else. It's
+     * object as {@link #getConfiguration()}, but sometimes it's a {@link TemplateConfiguration}, or something else. It's
      * never {@code null}.
      * 
      * @since 2.3.24
@@ -663,7 +663,7 @@ public class Template extends Configurable {
      * Returns the output format (see {@link Configuration#setOutputFormat(OutputFormat)}) used for this template.
      * The output format of a template can come from various places, in order of increasing priority:
      * {@link Configuration#getOutputFormat()}, {@link ParserConfiguration#getOutputFormat()} (which is usually
-     * provided by {@link Configuration#getTemplateConfigurers()}) and the {@code #ftl} header's {@code output_format}
+     * provided by {@link Configuration#getTemplateConfigurations()}) and the {@code #ftl} header's {@code output_format}
      * option in the template.
      * 
      * @since 2.3.24
@@ -683,7 +683,7 @@ public class Template extends Configurable {
      * Returns if the template actually uses auto-escaping (see {@link Configuration#setAutoEscapingPolicy(int)}). This value
      * is decided by the parser based on the actual {@link OutputFormat}, and the auto-escaping enums, in order of
      * increasing priority: {@link Configuration#getAutoEscapingPolicy()}, {@link ParserConfiguration#getAutoEscapingPolicy()}
-     * (which is usually provided by {@link Configuration#getTemplateConfigurers()}), and finally on the {@code #ftl}
+     * (which is usually provided by {@link Configuration#getTemplateConfigurations()}), and finally on the {@code #ftl}
      * header's {@code auto_esc} option in the template.
      * 
      * @since 2.3.24

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ea5c47d1/src/manual/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/book.xml b/src/manual/book.xml
index 0b80c41..c561322 100644
--- a/src/manual/book.xml
+++ b/src/manual/book.xml
@@ -7991,10 +7991,10 @@ myCfg.setDefaultEncoding("UTF-8");</programlisting>
           <listitem>
             <para><literal>Template</literal> layer: Settings on individual
             templates are normally set by <link
-            linkend="pgui_config_templateconfigurers">template configurers
-            (see them in their own chapter)</link>, which basically associate
-            setting assignments to template name (template path) patterns.
-            There's a deviation from this approach with the
+            linkend="pgui_config_templateconfigurations">template
+            configurations (see them in their own chapter)</link>, which
+            basically associate setting assignments to template name (template
+            path) patterns. There's a deviation from this approach with the
             <literal>locale</literal> setting, because that you can also
             specify to <literal>Configuration.getTemplate(...)</literal> as
             parameter, to get the template for the requested locale (so called
@@ -8689,13 +8689,13 @@ c</programlisting>
         </section>
       </section>
 
-      <section xml:id="pgui_config_templateconfigurers">
-        <title>Template configurers</title>
+      <section xml:id="pgui_config_templateconfigurations">
+        <title>Template configurations</title>
 
-        <para>Template configurers refers to the
-        <literal>template_configurers</literal> setting of
+        <para><quote>Template configurations</quote> refers to the
+        <literal>template_configurations</literal> setting of
         <literal>Configuration</literal>
-        (<literal>Configuration.setTemplateConfigurers(<replaceable>...</replaceable>)</literal>).
+        (<literal>Configuration.setTemplateConfigurations(<replaceable>...</replaceable>)</literal>).
         This setting lets you override individual settings coming from the
         common <literal>Configuration</literal> object, depending on the name
         (path) of the template.</para>
@@ -8713,13 +8713,13 @@ c</programlisting>
 
         <itemizedlist>
           <listitem>
-            <para><literal>TemplateConfigurer</literal>-s: These store the
+            <para><literal>TemplateConfiguration</literal>-s: These store the
             actual setting assignments that you want to do. For example, this
-            <literal>TemplateConfigurer</literal> will set the encoding and
+            <literal>TemplateConfiguration</literal> will set the encoding and
             the output format of the matched template (and leave all other
             settings of it alone):</para>
 
-            <programlisting role="unspecified">TemplateConfigurer tcUTF8XML = new TemplateConfigurer();
+            <programlisting role="unspecified">TemplateConfiguration tcUTF8XML = new TemplateConfiguration();
 tc.setEncoding("utf-8");
 tc.setOutputFormat(XMLOutputFormat.INSTANCE);</programlisting>
           </listitem>
@@ -8736,11 +8736,11 @@ tc.setOutputFormat(XMLOutputFormat.INSTANCE);</programlisting>
           </listitem>
 
           <listitem>
-            <para><literal>TemplateConfigurerFactory</literal>-es: This is
-            what connects <literal>TemplateConfigurer</literal> and
+            <para><literal>TemplateConfigurationFactory</literal>-es: This is
+            what connects <literal>TemplateConfiguration</literal> and
             <literal>TemplateSourceMatcher</literal> together. This is the
-            Java type of the <literal>template_configurers</literal> setting.
-            See the examples below for more.</para>
+            Java type of the <literal>template_configurations</literal>
+            setting. See the examples below for more.</para>
           </listitem>
         </itemizedlist>
 
@@ -8748,12 +8748,12 @@ tc.setOutputFormat(XMLOutputFormat.INSTANCE);</programlisting>
           <title>Example 1</title>
 
           <para>This setup combines our earlier two example object with a
-          <literal>ConditionalTemplateConfigurerFactory</literal>, causing all
-          templates with <literal>xml</literal> extension to get UTF-8
+          <literal>ConditionalTemplateConfigurationFactory</literal>, causing
+          all templates with <literal>xml</literal> extension to get UTF-8
           encoding and XML output format:</para>
 
-          <programlisting role="unspecified">cfg.setTemplateConfigurers(
-        new ConditionalTemplateConfigurerFactory(
+          <programlisting role="unspecified">cfg.setTemplateConfigurations(
+        new ConditionalTemplateConfigurationFactory(
                 new FileExtensionMatcher("xml"),
                 tcUTF8XML));</programlisting>
 
@@ -8763,10 +8763,10 @@ tc.setOutputFormat(XMLOutputFormat.INSTANCE);</programlisting>
           key value pairs (the <literal>\</literal>-s are prescribed by the
           Java Properties file format):</para>
 
-          <programlisting role="unspecified">templateConfigurers = \
-    ConditionalTemplateConfigurerFactory( \
+          <programlisting role="unspecified">templateConfigurations = \
+    ConditionalTemplateConfigurationFactory( \
         FileExtensionMatcher("xml"), \
-        TemplateConfigurer( \
+        TemplateConfiguration( \
             encoding = "utf-8", \
             outputFormat = XMLOutputFormat() \
         ) \
@@ -8784,20 +8784,20 @@ tc.setOutputFormat(XMLOutputFormat.INSTANCE);</programlisting>
           Subject templates must get <literal>plainText</literal> output
           format, while body templates must get <literal>HTML</literal> output
           format. So we have to make a choice here, and that's when you need a
-          <literal>FirstMatchTemplateConfigurerFactory</literal>.</para>
+          <literal>FirstMatchTemplateConfigurationFactory</literal>.</para>
 
           <para>Assuming <literal>cfg</literal> stores the shared
           <literal>Configuration</literal> singleton, you set this up like
           this:</para>
 
-          <programlisting role="unspecified">cfg.setTemplateConfigurers(
-        new ConditionalTemplateConfigurerFactory(
+          <programlisting role="unspecified">cfg.setTemplateConfigurations(
+        new ConditionalTemplateConfigurationFactory(
                 new PathGlobMatcher("mail/**"),
-                new FirstMatchTemplateConfigurerFactory(
-                        new ConditionalTemplateConfigurerFactory(
+                new FirstMatchTemplateConfigurationFactory(
+                        new ConditionalTemplateConfigurationFactory(
                                 new FileNameGlobMatcher("*.subject.*"),
                                 tcSubject),
-                        new ConditionalTemplateConfigurerFactory(
+                        new ConditionalTemplateConfigurationFactory(
                                 new FileNameGlobMatcher("*.body.*"),
                                 tcBody)
                         )
@@ -8810,17 +8810,17 @@ tc.setOutputFormat(XMLOutputFormat.INSTANCE);</programlisting>
           key value pairs (the <literal>\</literal>-s are prescribed by the
           Java Properties file format only):</para>
 
-          <programlisting role="unspecified">templateConfigurers = \
-    ConditionalTemplateConfigurerFactory( \
+          <programlisting role="unspecified">templateConfigurations = \
+    ConditionalTemplateConfigurationFactory( \
         PathGlobMatcher("mail/**"), \
-        FirstMatchTemplateConfigurerFactory( \
-            ConditionalTemplateConfigurerFactory( \
+        FirstMatchTemplateConfigurationFactory( \
+            ConditionalTemplateConfigurationFactory( \
                 FileNameGlobMatcher("*.subject.*"), \
-                TemplateConfigurer(outputFormat = PlainTextOutputFormat()) \
+                TemplateConfiguration(outputFormat = PlainTextOutputFormat()) \
             ), \
-            ConditionalTemplateConfigurerFactory( \
+            ConditionalTemplateConfigurationFactory( \
                 FileNameGlobMatcher("*.body.*"), \
-                TemplateConfigurer(outputFormat = HTMLOutputFormat()) \
+                TemplateConfiguration(outputFormat = HTMLOutputFormat()) \
             ), \
             noMatchErrorDetails = 'Mail template names must contain ".subject." or ".body."!' \
         ) \
@@ -8858,42 +8858,42 @@ tc.setOutputFormat(XMLOutputFormat.INSTANCE);</programlisting>
 
           <para>Here we have 3 independent concerns, and possibly multiple (or
           none) of those apply to a template; that's when you need a
-          <literal>MergingTemplateConfigurerFactory</literal>. The last point
-          describes a rule where you have mutually exclusive choices; that's
-          when you need a
-          <literal>FirstMatchTemplateConfigurerFactory</literal>, but this
+          <literal>MergingTemplateConfigurationFactory</literal>. The last
+          point describes a rule where you have mutually exclusive choices;
+          that's when you need a
+          <literal>FirstMatchTemplateConfigurationFactory</literal>, but this
           time no choice is also allowed. Here's the source code, assuming
           <literal>cfg</literal> stores the shared
           <literal>Configuration</literal> instance:</para>
 
-          <programlisting role="unspecified">TemplateConfigurer tcStats = new TemplateConfigurer();
+          <programlisting role="unspecified">TemplateConfiguration tcStats = new TemplateConfiguration();
 tcStats.setDateTimeFormat("iso");
 tcStats.setDateFormat("iso");
 tcStats.setTimeFormat("iso");
 tcStats.setTimeZone(DateUtil.UTC);
 
-TemplateConfigurer tcMail = new TemplateConfigurer();
+TemplateConfiguration tcMail = new TemplateConfiguration();
 tcMail.setEncoding("utf-8");
 
-TemplateConfigurer tcHTML = new TemplateConfigurer();
+TemplateConfiguration tcHTML = new TemplateConfiguration();
 tcHTML.setOutputFormat(HTMLOutputFormat.INSTANCE);
 
-TemplateConfigurer tcXML = new TemplateConfigurer();
+TemplateConfiguration tcXML = new TemplateConfiguration();
 tcXML.setOutputFormat(XMLOutputFormat.INSTANCE);
 
-cfg.setTemplateConfigurers(
-        new MergingTemplateConfigurerFactory(
-                new ConditionalTemplateConfigurerFactory(
+cfg.setTemplateConfigurations(
+        new MergingTemplateConfigurationFactory(
+                new ConditionalTemplateConfigurationFactory(
                         new FileNameGlobMatcher("*.stats.*"),
                         tcStats),
-                new ConditionalTemplateConfigurerFactory(
+                new ConditionalTemplateConfigurationFactory(
                         new PathGlobMatcher("mail/**"),
                         tcMail),
-                new FirstMatchTemplateConfigurerFactory(
-                        new ConditionalTemplateConfigurerFactory(
+                new FirstMatchTemplateConfigurationFactory(
+                        new ConditionalTemplateConfigurationFactory(
                                 new FileExtensionMatcher("xml"),
                                 tcXML),
-                        new ConditionalTemplateConfigurerFactory(
+                        new ConditionalTemplateConfigurationFactory(
                                 new OrMatcher(
                                         new FileExtensionMatcher("html"),
                                         new FileExtensionMatcher("htm")),
@@ -8907,32 +8907,32 @@ cfg.setTemplateConfigurers(
           key value pairs (the <literal>\</literal>-s are prescribed by the
           Java Properties file format only):</para>
 
-          <programlisting role="unspecified">templateConfigurers = \
-    MergingTemplateConfigurerFactory( \
-        ConditionalTemplateConfigurerFactory( \
+          <programlisting role="unspecified">templateConfigurations = \
+    MergingTemplateConfigurationFactory( \
+        ConditionalTemplateConfigurationFactory( \
             FileNameGlobMatcher("*.stats.*"), \
-            TemplateConfigurer( \
+            TemplateConfiguration( \
                 dateTimeFormat = "iso", \
                 dateFormat = "iso", \
                 timeFormat = "iso", \
                 timeZone = TimeZone("UTC") \
             ) \
         ), \
-        ConditionalTemplateConfigurerFactory( \
+        ConditionalTemplateConfigurationFactory( \
             PathGlobMatcher("mail/**"), \
-            TemplateConfigurer(encoding = "utf-8") \
+            TemplateConfiguration(encoding = "utf-8") \
         ), \
-        FirstMatchTemplateConfigurerFactory( \
-            ConditionalTemplateConfigurerFactory( \
+        FirstMatchTemplateConfigurationFactory( \
+            ConditionalTemplateConfigurationFactory( \
                 FileExtensionMatcher("xml"), \
-                TemplateConfigurer(outputFormat = XMLOutputFormat()) \
+                TemplateConfiguration(outputFormat = XMLOutputFormat()) \
             ), \
-            ConditionalTemplateConfigurerFactory( \
+            ConditionalTemplateConfigurationFactory( \
                 OrMatcher( \
                     FileExtensionMatcher("html"), \
                     FileExtensionMatcher("htm") \
                 ), \
-                TemplateConfigurer(outputFormat = HTMLOutputFormat()) \
+                TemplateConfiguration(outputFormat = HTMLOutputFormat()) \
             ), \
             allowNoMatch = true \
         ) \
@@ -8970,7 +8970,7 @@ cfg.setTemplateConfigurers(
         file extensions is the recommended way of activating HTML and XML
         auto-escaping. You can also associate output formats to templates
         based on arbitrary name patterns with the <link
-        linkend="pgui_config_templateconfigurers"><literal>template_configurers</literal>
+        linkend="pgui_config_templateconfigurations"><literal>template_configurations</literal>
         setting</link>; see some examples of that below.</para>
 
         <para>There's another a related setting, called
@@ -9038,11 +9038,11 @@ cfg.setRecognizeStandardFileExtensions(true);</programlisting>
 
             <programlisting role="unspecified">// Where you initalize the Configuration singletion, add:
 
-TemplateConfigurer tcHTML = new TemplateConfigurer();
+TemplateConfiguration tcHTML = new TemplateConfiguration();
 tcHTML.setOutputFormat(HTMLOutputFormat.INSTANCE);
 
-cfg.setTemplateConfigurers(
-        new ConditionalTemplateConfigurerFactory(
+cfg.setTemplateConfigurations(
+        new ConditionalTemplateConfigurationFactory(
                 new PathGlobMatcher("mail/**"),
                 tcHTML));</programlisting>
 
@@ -9050,10 +9050,10 @@ cfg.setTemplateConfigurers(
             <literal>*.properties</literal> file (the <literal>\</literal>-s
             are required for the Java Properties file format only):</para>
 
-            <programlisting role="unspecified">templateConfigurers = \
-    ConditionalTemplateConfigurerFactory( \
+            <programlisting role="unspecified">templateConfigurations = \
+    ConditionalTemplateConfigurationFactory( \
         PathGlobMatcher("mail/**"), \
-        TemplateConfigurer(outputFormat = HTMLOutputFormat()))</programlisting>
+        TemplateConfiguration(outputFormat = HTMLOutputFormat()))</programlisting>
           </listitem>
 
           <listitem>
@@ -9067,26 +9067,26 @@ cfg.setTemplateConfigurers(
             <literal>cfg.getTemplate(<replaceable>...</replaceable>)</literal>,
             and not instantiating them yourself):</para>
 
-            <programlisting role="unspecified">TemplateConfigurer tcHTML = new TemplateConfigurer();
+            <programlisting role="unspecified">TemplateConfiguration tcHTML = new TemplateConfiguration();
 tcHTML.setOutputFormat(HTMLOutputFormat.INSTANCE);
 
-TemplateConfigurer tcXML = new TemplateConfigurer();
+TemplateConfiguration tcXML = new TemplateConfiguration();
 tcXML.setOutputFormat(XMLOutputFormat.INSTANCE);
 
-TemplateConfigurer tcRTF = new TemplateConfigurer();
+TemplateConfiguration tcRTF = new TemplateConfiguration();
 tcRTF.setOutputFormat(RTFOutputFormat.INSTANCE);
 
-cfg.setTemplateConfigurers(
-        new FirstMatchTemplateConfigurerFactory(
-                new ConditionalTemplateConfigurerFactory(
+cfg.setTemplateConfigurations(
+        new FirstMatchTemplateConfigurationFactory(
+                new ConditionalTemplateConfigurationFactory(
                         new FileExtensionMatcher("xml"),
                         tcXML),
-                new ConditionalTemplateConfigurerFactory(
+                new ConditionalTemplateConfigurationFactory(
                         new OrMatcher(
                                 new FileExtensionMatcher("html"),
                                 new FileExtensionMatcher("htm")),
                         tcHTML),
-                new ConditionalTemplateConfigurerFactory(
+                new ConditionalTemplateConfigurationFactory(
                         new FileExtensionMatcher("rtf"),
                         tcRTF)
         ).allowNoMatch(true)
@@ -9096,26 +9096,26 @@ cfg.setTemplateConfigurers(
             <literal>*.properties</literal> file (the <literal>\</literal>-s
             are required for the Java Properties file format only):</para>
 
-            <programlisting role="unspecified">templateConfigurers = \
-    FirstMatchTemplateConfigurerFactory( \
-        ConditionalTemplateConfigurerFactory( \
+            <programlisting role="unspecified">templateConfigurations = \
+    FirstMatchTemplateConfigurationFactory( \
+        ConditionalTemplateConfigurationFactory( \
             FileExtensionMatcher("xml"), \
-            TemplateConfigurer(outputFormat = XMLOutputFormat())), \
-        ConditionalTemplateConfigurerFactory( \
+            TemplateConfiguration(outputFormat = XMLOutputFormat())), \
+        ConditionalTemplateConfigurationFactory( \
             OrMatcher( \
                 FileExtensionMatcher("html"), \
                 FileExtensionMatcher("htm")), \
-            TemplateConfigurer(outputFormat = HTMLOutputFormat())), \
-        ConditionalTemplateConfigurerFactory( \
+            TemplateConfiguration(outputFormat = HTMLOutputFormat())), \
+        ConditionalTemplateConfigurationFactory( \
             FileExtensionMatcher("rtf"), \
-            TemplateConfigurer(outputFormat = RTFOutputFormat())), \
+            TemplateConfiguration(outputFormat = RTFOutputFormat())), \
         allowNoMatch = true)</programlisting>
           </listitem>
         </itemizedlist>
 
         <para>(You can find some more complex
-        <literal>template_configurers</literal> setups <link
-        linkend="pgui_config_templateconfigurers">here...</link>)</para>
+        <literal>template_configurations</literal> setups <link
+        linkend="pgui_config_templateconfigurations">here...</link>)</para>
       </section>
 
       <section xml:id="pgui_config_custom_formats">
@@ -19091,9 +19091,9 @@ All rights reserved.</emphasis></programlisting>
               included template. You shouldn't use this option anymore; if
               different template use different encodings, then the programmers
               should associated the encoding to the templates via
-              <literal>Configuration.setTemplateConfigurers(<replaceable>...</replaceable>)</literal>-s
+              <literal>Configuration.setTemplateConfigurations(<replaceable>...</replaceable>)</literal>-s
               (which also overrides that you specify here). If
-              <literal>Configuration.setTemplateConfigurers(<replaceable>...</replaceable>)</literal>
+              <literal>Configuration.setTemplateConfigurations(<replaceable>...</replaceable>)</literal>
               doesn't specify an encoding for the included template, then the
               included file inherits the encoding (the charset) of the
               top-level template, unless you specify an encoding with this
@@ -25881,11 +25881,11 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
 
             <listitem>
               <para>Added new configuration setting:
-              <literal>template_configurers</literal>. This allows overriding
-              the settings coming from the shared
+              <literal>template_configurations</literal>. This allows
+              overriding the settings coming from the shared
               <literal>Configuration</literal> object for individual
               templates, based on template name patterns. <link
-              linkend="pgui_config_templateconfigurers">See more
+              linkend="pgui_config_templateconfigurations">See more
               here...</link></para>
             </listitem>
 
@@ -25921,7 +25921,7 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
                   <literal>XML</literal>, <literal>plainText</literal>, etc.)
                   that governs auto-escaping. The output format can be
                   different for different templates, using the
-                  <literal>template_configurers</literal> setting (<link
+                  <literal>template_configurations</literal> setting (<link
                   linkend="pgui_config_outputformatsautoesc">see here
                   how...</link>).</para>
                 </listitem>
@@ -25997,7 +25997,7 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
                   <literal>custom_number_formats</literal> and
                   <literal>custom_date_formats</literal> settings can be set
                   per-template (via the new
-                  <literal>template_configurers</literal> settings) or
+                  <literal>template_configurations</literal> settings) or
                   per-<literal>Environment</literal> too, thus
                   <literal>@foo</literal> can mean something different in
                   different templates.</para>
@@ -26508,6 +26508,17 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
             </listitem>
 
             <listitem>
+              <para>The new <literal>TemplateConfigurer</literal> class was
+              renamed to <literal>TemplateConfiguration</literal>, and the
+              related configuration setting from
+              <literal>template_configurers</literal> to
+              <literal>template_configurations</literal>. Also, the
+              <literal>TemplateConfigurer.configure</literal> method was
+              renamed to
+              <literal>TemplateConfiguration.apply</literal>.</para>
+            </listitem>
+
+            <listitem>
               <para>Bug fixed: It wasn't well defined when a Java
               <literal>Iterator</literal> counts as empty. Depending on what
               <literal>ObjectWrapper</literal> you are using, one of these