Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 06722200CA5 for ; Sat, 10 Jun 2017 09:55:16 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 04FA5160BDA; Sat, 10 Jun 2017 07:55:16 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 7E0F7160BD7 for ; Sat, 10 Jun 2017 09:55:14 +0200 (CEST) Received: (qmail 18704 invoked by uid 500); 10 Jun 2017 07:55:13 -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 18695 invoked by uid 99); 10 Jun 2017 07:55:13 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 10 Jun 2017 07:55:13 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id 29DFFC0E4F for ; Sat, 10 Jun 2017 07:55:13 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -4.222 X-Spam-Level: X-Spam-Status: No, score=-4.222 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001, SPF_PASS=-0.001] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id bew2w4tbihc1 for ; Sat, 10 Jun 2017 07:55:07 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 564B65FAC9 for ; Sat, 10 Jun 2017 07:55:05 +0000 (UTC) Received: (qmail 18677 invoked by uid 99); 10 Jun 2017 07:55:05 -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, 10 Jun 2017 07:55:05 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id D0DD2DF9FD; Sat, 10 Jun 2017 07:55:03 +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 Message-Id: <97c179bfb1a24b0e92f33a412d95be1c@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: incubator-freemarker git commit: Added `protected Xxx getImplictXxx()` methods to Configuration.ExtendableBuilder for Map and List settings. These allow adding values to these Map-s/List-s over those explictly set by the user who configure FreeMarker. Th Date: Sat, 10 Jun 2017 07:55:03 +0000 (UTC) archived-at: Sat, 10 Jun 2017 07:55:16 -0000 Repository: incubator-freemarker Updated Branches: refs/heads/3 18fd4e8f2 -> 164f292ef Added `protected Xxx getImplictXxx()` methods to Configuration.ExtendableBuilder for Map and List settings. These allow adding values to these Map-s/List-s over those explictly set by the user who configure FreeMarker. The default implemention doesn't utilize these, except for sharedVariables to add the legacy directives like "compress" (though these will be eventually removed). Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/164f292e Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/164f292e Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/164f292e Branch: refs/heads/3 Commit: 164f292efe9d59e2dc818d544ec4388df064afc5 Parents: 18fd4e8 Author: ddekany Authored: Sat Jun 10 09:54:56 2017 +0200 Committer: ddekany Committed: Sat Jun 10 09:54:56 2017 +0200 ---------------------------------------------------------------------- .../freemarker/core/ConfigurationTest.java | 91 ++++++++++- .../apache/freemarker/core/Configuration.java | 150 ++++++++++++++++--- .../core/MutableProcessingConfiguration.java | 6 +- .../org/apache/freemarker/core/Template.java | 4 +- .../freemarker/core/TemplateConfiguration.java | 64 +------- .../freemarker/core/util/_CollectionUtil.java | 51 +++++++ 6 files changed, 281 insertions(+), 85 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/164f292e/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java ---------------------------------------------------------------------- diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java index fa1c4d2..7cf205f 100644 --- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java +++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java @@ -55,6 +55,8 @@ import org.apache.freemarker.core.outputformat.impl.XMLOutputFormat; import org.apache.freemarker.core.templateresolver.CacheStorageWithGetSize; import org.apache.freemarker.core.templateresolver.ConditionalTemplateConfigurationFactory; import org.apache.freemarker.core.templateresolver.FileNameGlobMatcher; +import org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory; +import org.apache.freemarker.core.templateresolver.TemplateConfigurationFactoryException; import org.apache.freemarker.core.templateresolver.TemplateLookupContext; import org.apache.freemarker.core.templateresolver.TemplateLookupResult; import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; @@ -1454,6 +1456,51 @@ public class ConfigurationTest extends TestCase { assertNotEquals(mutableValue, immutableValue); // No aliasing } + @Test + public void testImpliedSettingValues() throws IOException, TemplateConfigurationFactoryException { + Configuration cfg = new ImpliedSettingValuesTestBuilder().build(); + + assertEquals("Y,N", cfg.getTemplateConfigurations().get("t.yn", null).getBooleanFormat()); + assertNotNull(cfg.getCustomNumberFormat("hex")); + assertNotNull(cfg.getCustomDateFormat("epoch")); + assertEquals(ImmutableMap.of("lib", "lib.ftl"), cfg.getAutoImports()); + assertEquals(ImmutableList.of("inc.ftl"), cfg.getAutoIncludes()); + assertEquals(ImmutableMap.of("v", 1), cfg.getSharedVariables()); + } + + @Test + public void testImpliedSettingValues2() throws IOException, TemplateConfigurationFactoryException { + Configuration cfg = new ImpliedSettingValuesTestBuilder() + .templateConfigurations( + new ConditionalTemplateConfigurationFactory( + new FileNameGlobMatcher("*.jn"), + new TemplateConfiguration.Builder().booleanFormat("J,N").build() + ) + ) + .customNumberFormats(ImmutableMap.of("baseN", BaseNTemplateNumberFormatFactory.INSTANCE)) + .customDateFormats(ImmutableMap.of("epochDiv", EpochMillisDivTemplateDateFormatFactory.INSTANCE)) + .autoImports(ImmutableMap.of("lib2", "lib2.ftl")) + .autoIncludes(ImmutableList.of("inc2.ftl")) + .sharedVariables(ImmutableMap.of("v2", 2)) + .build(); + + TemplateConfigurationFactory tcf = cfg.getTemplateConfigurations(); + assertEquals("Y,N", tcf.get("t.yn", null).getBooleanFormat()); + assertEquals("J,N", tcf.get("t.jn", null).getBooleanFormat()); + + assertNotNull(cfg.getCustomNumberFormat("hex")); + assertNotNull(cfg.getCustomNumberFormat("baseN")); + + assertNotNull(cfg.getCustomDateFormat("epoch")); + assertNotNull(cfg.getCustomDateFormat("epochDiv")); + + assertEquals(ImmutableMap.of("lib", "lib.ftl", "lib2", "lib2.ftl"), cfg.getAutoImports()); + + assertEquals(ImmutableList.of("inc.ftl", "inc2.ftl"), cfg.getAutoIncludes()); + + assertEquals(ImmutableMap.of("v", 1, "v2", 2), cfg.getSharedVariables()); + } + @SuppressWarnings("boxing") private void assertStartsWith(List list, List headList) { int index = 0; @@ -1483,5 +1530,47 @@ public class ConfigurationTest extends TestCase { } } - + + private static class ImpliedSettingValuesTestBuilder + extends Configuration.ExtendableBuilder { + + ImpliedSettingValuesTestBuilder() { + super(Configuration.VERSION_3_0_0); + } + + @Override + protected TemplateConfigurationFactory getImpliedTemplateConfigurations() { + return new ConditionalTemplateConfigurationFactory( + new FileNameGlobMatcher("*.yn"), + new TemplateConfiguration.Builder().booleanFormat("Y,N").build()); + } + + @Override + protected Map getImpliedCustomNumberFormats() { + return ImmutableMap.of( + "hex", HexTemplateNumberFormatFactory.INSTANCE); + } + + @Override + protected Map getImpliedCustomDateFormats() { + return ImmutableMap.of( + "epoch", EpochMillisTemplateDateFormatFactory.INSTANCE); + } + + @Override + protected Map getImpliedAutoImports() { + return ImmutableMap.of("lib", "lib.ftl"); + } + + @Override + protected List getImpliedAutoIncludes() { + return ImmutableList.of("inc.ftl"); + } + + @Override + protected Map getImplicitSharedVariables() { + return ImmutableMap.of("v", 1); + } + } + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/164f292e/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java index e7e5947..93bd1ff 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java @@ -64,6 +64,7 @@ import org.apache.freemarker.core.outputformat.impl.XMLOutputFormat; import org.apache.freemarker.core.templateresolver.CacheStorage; import org.apache.freemarker.core.templateresolver.GetTemplateResult; import org.apache.freemarker.core.templateresolver.MalformedTemplateNameException; +import org.apache.freemarker.core.templateresolver.MergingTemplateConfigurationFactory; import org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory; import org.apache.freemarker.core.templateresolver.TemplateLoader; import org.apache.freemarker.core.templateresolver.TemplateLookupContext; @@ -372,26 +373,15 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc ObjectWrapper objectWrapper = builder.getObjectWrapper(); { - Map sharedVariables = builder.getSharedVariables(); + Map sharedVariables = _CollectionUtil.mergeImmutableMaps(builder + .getImplicitSharedVariables(), builder.getSharedVariables(), false); HashMap wrappedSharedVariables = new HashMap<>( - (sharedVariables.size() + 5 /* [FM3] 5 legacy vars */) * 4 / 3 + 1, 0.75f); - - // TODO [FM3] Get rid of this - wrappedSharedVariables.put("capture_output", new CaptureOutput()); - wrappedSharedVariables.put("compress", StandardCompress.INSTANCE); - wrappedSharedVariables.put("html_escape", new HtmlEscape()); - wrappedSharedVariables.put("normalize_newlines", new NormalizeNewlines()); - wrappedSharedVariables.put("xml_escape", new XmlEscape()); - - // In case the inherited sharedVariables aren't empty, we want to merge the two maps: - wrapAndPutSharedVariables(wrappedSharedVariables, builder.getDefaultSharedVariables(), - objectWrapper); - if (builder.isSharedVariablesSet()) { - wrapAndPutSharedVariables(wrappedSharedVariables, sharedVariables, objectWrapper); - } + sharedVariables.size() * 4 / 3 + 1, 0.75f); + wrapAndPutSharedVariables(wrappedSharedVariables, sharedVariables, objectWrapper); + this.wrappedSharedVariables = wrappedSharedVariables; - this.sharedVariables = Collections.unmodifiableMap(new LinkedHashMap<>(sharedVariables)); + this.sharedVariables = Collections.unmodifiableMap(sharedVariables); } // ParsingConfiguration settings: @@ -426,10 +416,14 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc showErrorTips = builder.getShowErrorTips(); apiBuiltinEnabled = builder.getAPIBuiltinEnabled(); logTemplateExceptions = builder.getLogTemplateExceptions(); - customDateFormats = builder.getCustomDateFormats(); - customNumberFormats = builder.getCustomNumberFormats(); - autoImports = builder.getAutoImports(); - autoIncludes = builder.getAutoIncludes(); + customDateFormats = _CollectionUtil.mergeImmutableMaps( + builder.getImpliedCustomDateFormats(), builder.getCustomDateFormats(), false); + customNumberFormats = _CollectionUtil.mergeImmutableMaps( + builder.getImpliedCustomNumberFormats(), builder.getCustomNumberFormats(), false); + autoImports = _CollectionUtil.mergeImmutableMaps( + builder.getImpliedAutoImports(), builder.getAutoImports(), true); + autoIncludes = _CollectionUtil.mergeImmutableLists( + builder.getImpliedAutoIncludes(), builder.getAutoIncludes(), true); lazyImports = builder.getLazyImports(); lazyAutoImports = builder.getLazyAutoImports(); customSettings = builder.getCustomSettings(false); @@ -474,11 +468,21 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc templateResolver, TEMPLATE_NAME_FORMAT_KEY, templateNameFormat); } - templateConfigurations = builder.getTemplateConfigurations(); + TemplateConfigurationFactory templateConfigurations = builder.getTemplateConfigurations(); if (!templateResolver.supportsTemplateConfigurationsSetting()) { checkSettingIsNullForThisTemplateResolver( templateResolver, TEMPLATE_CONFIGURATIONS_KEY, templateConfigurations); } + TemplateConfigurationFactory impliedTemplateConfigurations = builder.getImpliedTemplateConfigurations(); + if (impliedTemplateConfigurations != null) { + if (templateConfigurations != null) { + templateConfigurations = new MergingTemplateConfigurationFactory( + impliedTemplateConfigurations, templateConfigurations); + } else { + templateConfigurations = impliedTemplateConfigurations; + } + } + this.templateConfigurations = templateConfigurations; templateResolver.setDependencies(new TemplateResolverDependenciesImpl(this, templateResolver)); } @@ -2338,6 +2342,18 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc } /** + * The template configurations that will be added to the built {@link Configuration} before the ones + * coming from {@link #setCustomNumberFormats(Map)}}, where addition happens with + * {@link MergingTemplateConfigurationFactory}. When overriding this method, always + * consider adding to the return value of the super method, rather than replacing it. + * + * @return Maybe {@code null}. + */ + protected TemplateConfigurationFactory getImpliedTemplateConfigurations() { + return null; + } + + /** * Setter pair of {@link Configuration#getTemplateConfigurations()}. */ public void setTemplateConfigurations(TemplateConfigurationFactory templateConfigurations) { @@ -2515,10 +2531,38 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc return sharedVariables != null; } + /** + * The {@link Map} to use as shared variables if {@link #isSharedVariablesSet()} is {@code false}. + * + * @see #getImplicitSharedVariables() + */ protected Map getDefaultSharedVariables() { return Collections.emptyMap(); } + private static final Map DEFAULT_SHARED_VARIABLES; + static { + // TODO [FM3] Get rid of these + Map sharedVariables = new HashMap<>(); + sharedVariables.put("capture_output", new CaptureOutput()); + sharedVariables.put("compress", StandardCompress.INSTANCE); + sharedVariables.put("html_escape", new HtmlEscape()); + sharedVariables.put("normalize_newlines", new NormalizeNewlines()); + sharedVariables.put("xml_escape", new XmlEscape()); + DEFAULT_SHARED_VARIABLES = Collections.unmodifiableMap(sharedVariables); + } + + /** + * The shared variables that will be added to the built {@link Configuration} before the ones coming from + * {@link #getSharedVariables()}. When overriding this method, always consider adding to the return value + * of the super method, rather than replacing it. + * + * @return Immutable {@link Map}; not {@code null} + */ + protected Map getImplicitSharedVariables() { + return DEFAULT_SHARED_VARIABLES; + } + /** * Setter pair of {@link Configuration#getSharedVariables()}. * @@ -2669,11 +2713,27 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc return Collections.emptyMap(); } + /** + * {@inheritDoc} + * + * @see #getImpliedCustomNumberFormats() + */ @Override protected TemplateNumberFormatFactory getDefaultCustomNumberFormat(String name) { return null; } + /** + * The custom number formats that will be added to the built {@link Configuration} before the ones coming from + * {@link #getCustomNumberFormats()}. When overriding this method, always consider adding to the return + * value of the super method, rather than replacing it. + * + * @return Immutable {@link Map}; not {@code null} + */ + protected Map getImpliedCustomNumberFormats() { + return Collections.emptyMap(); + } + @Override protected String getDefaultBooleanFormat() { return TemplateBooleanFormat.C_TRUE_FALSE; @@ -2694,11 +2754,27 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc return ""; } + /** + * {@inheritDoc} + * + * @see #getImpliedCustomDateFormats() + */ @Override protected Map getDefaultCustomDateFormats() { return Collections.emptyMap(); } + /** + * The custom date formats that will be added to the built {@link Configuration} before the ones coming from + * {@link #getCustomDateFormats()}. When overriding this method, always consider adding to the return value + * of the super method, rather than replacing it. + * + * @return Immutable {@link Map}; not {@code null} + */ + protected Map getImpliedCustomDateFormats() { + return Collections.emptyMap(); + } + @Override protected TemplateDateFormatFactory getDefaultCustomDateFormat(String name) { return null; @@ -2781,16 +2857,46 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc return null; } + /** + * {@inheritDoc} + * + * @see #getImpliedAutoImports() + */ @Override protected Map getDefaultAutoImports() { return Collections.emptyMap(); } + /** + * The auto-imports that will be added to the built {@link Configuration} before the ones coming from + * {@link #getSharedVariables()}. When overriding this method, always consider adding to the return value + * of the super method, rather than replacing it. + */ + protected Map getImpliedAutoImports() { + return Collections.emptyMap(); + } + + /** + * {@inheritDoc} + * + * @see #getImpliedAutoIncludes() + */ @Override protected List getDefaultAutoIncludes() { return Collections.emptyList(); } + /** + * The imports that will be added to the built {@link Configuration} before the ones coming from + * {@link #getAutoIncludes()}. When overriding this method, always consider adding to the return + * value of the super method, rather than replacing it. + * + * @return Immutable {@link List}; not {@code null} + */ + protected List getImpliedAutoIncludes() { + return Collections.emptyList(); + } + @Override protected Object getDefaultCustomSetting(Serializable key, Object defaultValue, boolean useDefaultValue) { if (useDefaultValue) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/164f292e/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java index 75eac65..98d927f 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableProcessingConfiguration.java @@ -555,7 +555,7 @@ public abstract class MutableProcessingConfiguration value) { + public SelfT customNumberFormats(Map value) { setCustomNumberFormats(value); return self(); } @@ -619,7 +619,7 @@ public abstract class MutableProcessingConfiguration value) { + public SelfT customDateFormats(Map value) { setCustomDateFormats(value); return self(); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/164f292e/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java index 3a0122d..8113eb8 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java @@ -661,7 +661,7 @@ public class Template implements ProcessingConfiguration, CustomStateScope { return customLookupCondition; } - // TODO [FM3] Should not be public, should be final field + // TODO [FM3] Should be gone; see comment above the lookupLocale field /** * Mostly only used internally; setter pair of {@link #getCustomLookupCondition()}. This meant to be called directly * after instantiating the template with its constructor, after a successfull lookup that used this condition. So @@ -803,7 +803,7 @@ public class Template implements ProcessingConfiguration, CustomStateScope { return tCfg != null && tCfg.isLocaleSet() ? tCfg.getLocale() : cfg.getLocale(); } - // TODO [FM3] Temporary hack; See comment above the locale field + // TODO [FM3] Temporary hack; See comment above the lookupLocale field public void setLookupLocale(Locale lookupLocale) { this.lookupLocale = lookupLocale; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/164f292e/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java index 4aaa7c2..284ec6b 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java @@ -21,20 +21,16 @@ package org.apache.freemarker.core; import java.io.Reader; import java.io.Serializable; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; import java.util.TimeZone; import org.apache.freemarker.core.arithmetic.ArithmeticEngine; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.outputformat.OutputFormat; import org.apache.freemarker.core.util.CommonBuilder; +import org.apache.freemarker.core.util._CollectionUtil; import org.apache.freemarker.core.valueformat.TemplateDateFormatFactory; import org.apache.freemarker.core.valueformat.TemplateNumberFormatFactory; @@ -139,52 +135,6 @@ public final class TemplateConfiguration implements ParsingAndProcessingConfigur tabSize = builder.isTabSizeSet() ? builder.getTabSize() : null; } - /** - * Adds two {@link Map}-s (keeping the iteration order); assuming the inputs are already unmodifiable and - * unchanging, it returns an unmodifiable and unchanging {@link Map} itself. - */ - private static Map mergeMaps(Map m1, Map m2, boolean overwriteUpdatesOrder) { - 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.size() + m2.size()) * 4 / 3 + 1, 0.75f); - mergedM.putAll(m1); - if (overwriteUpdatesOrder) { - for (K m2Key : m2.keySet()) { - mergedM.remove(m2Key); // So that duplicate keys are moved after m1 keys - } - } - mergedM.putAll(m2); - return Collections.unmodifiableMap(mergedM); - } - - /** - * Adds two {@link List}-s; assuming the inputs are already unmodifiable and unchanging, it returns an - * unmodifiable and unchanging {@link List} itself. - */ - private static List mergeLists(List list1, List list2, boolean skipDuplicatesInList1) { - if (list1 == null) return list2; - if (list2 == null) return list1; - if (list1.isEmpty()) return list2; - if (list2.isEmpty()) return list1; - - ArrayList mergedList = new ArrayList<>(list1.size() + list2.size()); - if (skipDuplicatesInList1) { - Set list2Set = new HashSet<>(list2); - for (String it : list1) { - if (!list2Set.contains(it)) { - mergedList.add(it); - } - } - } else { - mergedList.addAll(list1); - } - mergedList.addAll(list2); - return Collections.unmodifiableList(mergedList); - } - @Override public TagSyntax getTagSyntax() { if (!isTagSyntaxSet()) { @@ -858,13 +808,13 @@ public final class TemplateConfiguration implements ParsingAndProcessingConfigur setBooleanFormat(tc.getBooleanFormat()); } if (tc.isCustomDateFormatsSet()) { - setCustomDateFormats(mergeMaps( + setCustomDateFormats(_CollectionUtil.mergeImmutableMaps( isCustomDateFormatsSet() ? getCustomDateFormats() : null, tc.getCustomDateFormats(), false), true ); } if (tc.isCustomNumberFormatsSet()) { - setCustomNumberFormats(mergeMaps( + setCustomNumberFormats(_CollectionUtil.mergeImmutableMaps( isCustomNumberFormatsSet() ? getCustomNumberFormats() : null, tc.getCustomNumberFormats(), false), true); } @@ -941,24 +891,24 @@ public final class TemplateConfiguration implements ParsingAndProcessingConfigur setLazyAutoImports(tc.getLazyAutoImports()); } if (tc.isAutoImportsSet()) { - setAutoImports(mergeMaps( + setAutoImports(_CollectionUtil.mergeImmutableMaps( isAutoImportsSet() ? getAutoImports() : null, tc.isAutoImportsSet() ? tc.getAutoImports() : null, true), true); } if (tc.isAutoIncludesSet()) { - setAutoIncludes(mergeLists( + setAutoIncludes(_CollectionUtil.mergeImmutableLists( isAutoIncludesSet() ? getAutoIncludes() : null, tc.isAutoIncludesSet() ? tc.getAutoIncludes() : null, true), true); } - setCustomSettingsMap(mergeMaps( + setCustomSettingsMap(_CollectionUtil.mergeImmutableMaps( getCustomSettings(false), tc.getCustomSettings(false), - true)); + false)); } @Override http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/164f292e/freemarker-core/src/main/java/org/apache/freemarker/core/util/_CollectionUtil.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_CollectionUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_CollectionUtil.java index 1f91821..d5a6574 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/_CollectionUtil.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/_CollectionUtil.java @@ -23,8 +23,11 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * Don't use this; used internally by FreeMarker, might changes without notice. @@ -144,4 +147,52 @@ public class _CollectionUtil { return isMapKnownToBeUnmodifiable(map) ? map : Collections.unmodifiableMap(map); } + /** + * Adds two {@link Map}-s (keeping the iteration order); assuming the inputs are already unmodifiable and + * unchanging, it returns an unmodifiable and unchanging {@link Map} itself. + */ + public static Map mergeImmutableMaps(Map m1, Map m2, boolean keepOriginalOrder) { + if (m1 == null) return m2; + if (m2 == null) return m1; + if (m1.isEmpty()) return m2; + if (m2.isEmpty()) return m1; + + Map mergedM = keepOriginalOrder + ? new LinkedHashMap((m1.size() + m2.size()) * 4 / 3 + 1, 0.75f) + : new HashMap((m1.size() + m2.size()) * 4 / 3 + 1, 0.75f); + mergedM.putAll(m1); + if (keepOriginalOrder) { + for (K m2Key : m2.keySet()) { + mergedM.remove(m2Key); // So that duplicate keys are moved after m1 keys + } + } + mergedM.putAll(m2); + return Collections.unmodifiableMap(mergedM); + } + + /** + * Adds two {@link List}-s; assuming the inputs are already unmodifiable and unchanging, it returns an + * unmodifiable and unchanging {@link List} itself. + */ + public static List mergeImmutableLists(List list1, List list2, + boolean skipDuplicatesInList1) { + if (list1 == null) return list2; + if (list2 == null) return list1; + if (list1.isEmpty()) return list2; + if (list2.isEmpty()) return list1; + + ArrayList mergedList = new ArrayList<>(list1.size() + list2.size()); + if (skipDuplicatesInList1) { + Set list2Set = new HashSet<>(list2); + for (String it : list1) { + if (!list2Set.contains(it)) { + mergedList.add(it); + } + } + } else { + mergedList.addAll(list1); + } + mergedList.addAll(list2); + return Collections.unmodifiableList(mergedList); + } }