freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
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 GMT
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 <ddekany@apache.org>
Authored: Sat Jun 10 09:54:56 2017 +0200
Committer: ddekany <ddekany@apache.org>
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<String> list, List<String> headList) {
         int index = 0;
@@ -1483,5 +1530,47 @@ public class ConfigurationTest extends TestCase {
         }
         
     }
-    
+
+    private static class ImpliedSettingValuesTestBuilder
+            extends Configuration.ExtendableBuilder<ImpliedSettingValuesTestBuilder>
{
+
+        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<String, TemplateNumberFormatFactory> getImpliedCustomNumberFormats()
{
+            return ImmutableMap.<String, TemplateNumberFormatFactory>of(
+                    "hex", HexTemplateNumberFormatFactory.INSTANCE);
+        }
+
+        @Override
+        protected Map<String, TemplateDateFormatFactory> getImpliedCustomDateFormats()
{
+            return ImmutableMap.<String, TemplateDateFormatFactory>of(
+                    "epoch", EpochMillisTemplateDateFormatFactory.INSTANCE);
+        }
+
+        @Override
+        protected Map<String, String> getImpliedAutoImports() {
+            return ImmutableMap.of("lib", "lib.ftl");
+        }
+
+        @Override
+        protected List<String> getImpliedAutoIncludes() {
+            return ImmutableList.of("inc.ftl");
+        }
+
+        @Override
+        protected Map<String, Object> getImplicitSharedVariables() {
+            return ImmutableMap.<String, Object>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<String, Object> sharedVariables = builder.getSharedVariables();
+            Map<String, Object> sharedVariables = _CollectionUtil.mergeImmutableMaps(builder
+                    .getImplicitSharedVariables(), builder.getSharedVariables(), false);
 
             HashMap<String, TemplateModel> 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<String, Object> getDefaultSharedVariables() {
             return Collections.emptyMap();
         }
 
+        private static final Map<String, Object> DEFAULT_SHARED_VARIABLES;
+        static {
+            // TODO [FM3] Get rid of these
+            Map<String, Object> 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<String, Object> 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<String, TemplateNumberFormatFactory> 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<String, TemplateDateFormatFactory> 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<String, TemplateDateFormatFactory> 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<String, String> 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<String, String> getImpliedAutoImports() {
+            return Collections.emptyMap();
+        }
+
+        /**
+         * {@inheritDoc}
+         *
+         * @see #getImpliedAutoIncludes()
+         */
         @Override
         protected List<String> 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<String> 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<SelfT extends
MutableProces
     /**
      * Fluent API equivalent of {@link #setCustomNumberFormats(Map)}
      */
-    public SelfT customNumberFormats(Map<String, TemplateNumberFormatFactory> value)
{
+    public SelfT customNumberFormats(Map<String, ? extends TemplateNumberFormatFactory>
value) {
         setCustomNumberFormats(value);
         return self();
     }
@@ -619,7 +619,7 @@ public abstract class MutableProcessingConfiguration<SelfT extends
MutableProces
         int commaIdx = booleanFormat.indexOf(',');
         if (commaIdx == -1) {
             throw new IllegalArgumentException(
-                    "Setting value must be string that contains two comma-separated values
for true and false, " +
+                    "Setting value must be a string that contains two comma-separated values
for true and false, " +
                     "respectively.");
         }
         
@@ -826,7 +826,7 @@ public abstract class MutableProcessingConfiguration<SelfT extends
MutableProces
     /**
      * Fluent API equivalent of {@link #setCustomDateFormats(Map)}
      */
-    public SelfT customDateFormats(Map<String, TemplateDateFormatFactory> value) {
+    public SelfT customDateFormats(Map<String, ? extends TemplateDateFormatFactory>
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 <K,V> Map<K,V> mergeMaps(Map<K,V> m1, Map<K,V>
m2, boolean overwriteUpdatesOrder) {
-        if (m1 == null) return m2;
-        if (m2 == null) return m1;
-        if (m1.isEmpty()) return m2;
-        if (m2.isEmpty()) return m1;
-
-        LinkedHashMap<K, V> 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<String> mergeLists(List<String> list1, List<String>
list2, boolean skipDuplicatesInList1) {
-        if (list1 == null) return list2;
-        if (list2 == null) return list1;
-        if (list1.isEmpty()) return list2;
-        if (list2.isEmpty()) return list1;
-
-        ArrayList<String> mergedList = new ArrayList<>(list1.size() + list2.size());
-        if (skipDuplicatesInList1) {
-            Set<String> 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 <K,V> Map<K,V> mergeImmutableMaps(Map<K,V> m1, Map<K,V>
m2, boolean keepOriginalOrder) {
+        if (m1 == null) return m2;
+        if (m2 == null) return m1;
+        if (m1.isEmpty()) return m2;
+        if (m2.isEmpty()) return m1;
+
+        Map<K, V> mergedM = keepOriginalOrder
+                ? new LinkedHashMap<K, V>((m1.size() + m2.size()) * 4 / 3 + 1, 0.75f)
+                : new HashMap<K, V>((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<String> mergeImmutableLists(List<String> list1, List<String>
list2,
+            boolean skipDuplicatesInList1) {
+        if (list1 == null) return list2;
+        if (list2 == null) return list1;
+        if (list1.isEmpty()) return list2;
+        if (list2.isEmpty()) return list1;
+
+        ArrayList<String> mergedList = new ArrayList<>(list1.size() + list2.size());
+        if (skipDuplicatesInList1) {
+            Set<String> 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);
+    }
 }


Mime
View raw message