freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [6/8] 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
Date Sat, 19 Dec 2015 16:51:19 GMT
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
Commit: ea5c47d13366ccf8e90d69ddc94bdfa911719b77
Parents: 159bcb2
Author: ddekany <ddekany@apache.org>
Authored: Sat Dec 19 17:36:27 2015 +0100
Committer: ddekany <ddekany@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;
+    }
+    
+}


Mime
View raw message