Return-Path: X-Original-To: apmail-freemarker-notifications-archive@minotaur.apache.org Delivered-To: apmail-freemarker-notifications-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 124EA185FE for ; Sat, 19 Dec 2015 16:51:30 +0000 (UTC) Received: (qmail 63180 invoked by uid 500); 19 Dec 2015 16:51:30 -0000 Delivered-To: apmail-freemarker-notifications-archive@freemarker.apache.org Received: (qmail 63161 invoked by uid 500); 19 Dec 2015 16:51:30 -0000 Mailing-List: contact notifications-help@freemarker.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@freemarker.incubator.apache.org Delivered-To: mailing list notifications@freemarker.incubator.apache.org Received: (qmail 63152 invoked by uid 99); 19 Dec 2015 16:51:29 -0000 Received: from Unknown (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 19 Dec 2015 16:51:29 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id 6A1391A1469 for ; Sat, 19 Dec 2015 16:51:29 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.226 X-Spam-Level: * X-Spam-Status: No, score=1.226 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.554] autolearn=disabled Received: from mx1-eu-west.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id JevwkA8Bwlal for ; Sat, 19 Dec 2015 16:51:18 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-eu-west.apache.org (ASF Mail Server at mx1-eu-west.apache.org) with SMTP id B765825E2B for ; Sat, 19 Dec 2015 16:51:15 +0000 (UTC) Received: (qmail 62835 invoked by uid 99); 19 Dec 2015 16:51:14 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 19 Dec 2015 16:51:14 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 3B4C7E0991; Sat, 19 Dec 2015 16:51:14 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: ddekany@apache.org To: notifications@freemarker.incubator.apache.org Date: Sat, 19 Dec 2015 16:51:19 -0000 Message-Id: <613a41a5a54747b1a2ce3a41000e4bbc@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer 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 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 Authored: Sat Dec 19 17:36:27 2015 +0100 Committer: ddekany 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 { *
String value: {@code "default"} (case insensitive) for the default, or {@code "true"}, {@code "false"}, * {@code yes}, etc. * - *
  • {@code "template_configurers"}: - * See: {@link Configuration#setTemplateConfigurers(freemarker.cache.TemplateConfigurerFactory)}. + *

  • {@code "template_configurations"}: + * See: {@link Configuration#setTemplateConfigurations(freemarker.cache.TemplateConfigurationFactory)}. *
    String value: Interpreted as an object builder expression, * can be {@code null}. * @@ -1903,9 +1903,9 @@ public class Configurable { *

  • *

    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}. *

  • 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. + * + *

    + * 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. + * + *

    + * Note on encoding setting {@code encoding}: See {@link #setEncoding(String)}. + * + *

    + * 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}. + * + *

    + * 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: + * + *

      + *
    • 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). + * + *
    • {@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. + *
    + * + * @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}. + * + *

    + * 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. + * + *

    + * 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; + } + +}