You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/06/01 22:13:28 UTC
[3/5] incubator-freemarker git commit: Made some of the configuration
settings use immutable List-s and Map-s even in the Builder-s,
and removed methods that modified these maps:
Made some of the configuration settings use immutable List-s and Map-s even in the Builder-s, and removed methods that modified these maps:
- customDateFormats
- customNumberFormats
Optimized TemplateConfiguration.Builder.merge method to not copy the List-s and Map-s unnecessarily. Some further cleanup.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/eae27088
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/eae27088
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/eae27088
Branch: refs/heads/3
Commit: eae270885b5383d941384f96ed75bec116509898
Parents: a3cab7a
Author: ddekany <dd...@apache.org>
Authored: Fri May 26 12:24:51 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Fri May 26 12:53:20 2017 +0200
----------------------------------------------------------------------
.../core/TemplateConfigurationTest.java | 190 +++++++++----------
.../core/MutableProcessingConfiguration.java | 156 +++++++++++----
.../core/ProcessingConfiguration.java | 6 +-
.../freemarker/core/TemplateConfiguration.java | 34 +++-
.../freemarker/core/util/_CollectionUtil.java | 10 +-
5 files changed, 249 insertions(+), 147 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eae27088/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java
index e4d4390..c229655 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java
@@ -296,7 +296,7 @@ public class TemplateConfigurationTest {
Object value2 = SETTING_ASSIGNMENTS.get(propDesc2.getName());
propDesc2.getWriteMethod().invoke(tcb2, value2);
- tcb1.merge(tcb2);
+ tcb1.merge(tcb2.build());
if (propDesc1.getName().equals(propDesc2.getName()) && value1 instanceof List
&& !propDesc1.getName().equals("autoIncludes")) {
assertEquals("For " + propDesc1.getName(),
@@ -311,86 +311,86 @@ public class TemplateConfigurationTest {
@Test
public void testMergeMapSettings() throws Exception {
- TemplateConfiguration.Builder tc1 = new TemplateConfiguration.Builder();
- tc1.setCustomDateFormats(ImmutableMap.of(
+ TemplateConfiguration.Builder tcb1 = new TemplateConfiguration.Builder();
+ tcb1.setCustomDateFormats(ImmutableMap.of(
"epoch", EpochMillisTemplateDateFormatFactory.INSTANCE,
"x", LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE));
- tc1.setCustomNumberFormats(ImmutableMap.of(
+ tcb1.setCustomNumberFormats(ImmutableMap.of(
"hex", HexTemplateNumberFormatFactory.INSTANCE,
"x", LocaleSensitiveTemplateNumberFormatFactory.INSTANCE));
- tc1.setAutoImports(ImmutableMap.of("a", "a1.ftl", "b", "b1.ftl"));
+ tcb1.setAutoImports(ImmutableMap.of("a", "a1.ftl", "b", "b1.ftl"));
- TemplateConfiguration.Builder tc2 = new TemplateConfiguration.Builder();
- tc2.setCustomDateFormats(ImmutableMap.of(
+ TemplateConfiguration.Builder tcb2 = new TemplateConfiguration.Builder();
+ tcb2.setCustomDateFormats(ImmutableMap.of(
"loc", LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE,
"x", EpochMillisDivTemplateDateFormatFactory.INSTANCE));
- tc2.setCustomNumberFormats(ImmutableMap.of(
+ tcb2.setCustomNumberFormats(ImmutableMap.of(
"loc", LocaleSensitiveTemplateNumberFormatFactory.INSTANCE,
"x", BaseNTemplateNumberFormatFactory.INSTANCE));
- tc2.setAutoImports(ImmutableMap.of("b", "b2.ftl", "c", "c2.ftl"));
+ tcb2.setAutoImports(ImmutableMap.of("b", "b2.ftl", "c", "c2.ftl"));
- tc1.merge(tc2);
+ tcb1.merge(tcb2.build());
- Map<String, ? extends TemplateDateFormatFactory> mergedCustomDateFormats = tc1.getCustomDateFormats();
+ Map<String, ? extends TemplateDateFormatFactory> mergedCustomDateFormats = tcb1.getCustomDateFormats();
assertEquals(EpochMillisTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("epoch"));
assertEquals(LocAndTZSensitiveTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("loc"));
assertEquals(EpochMillisDivTemplateDateFormatFactory.INSTANCE, mergedCustomDateFormats.get("x"));
- Map<String, ? extends TemplateNumberFormatFactory> mergedCustomNumberFormats = tc1.getCustomNumberFormats();
+ Map<String, ? extends TemplateNumberFormatFactory> mergedCustomNumberFormats = tcb1.getCustomNumberFormats();
assertEquals(HexTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("hex"));
assertEquals(LocaleSensitiveTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("loc"));
assertEquals(BaseNTemplateNumberFormatFactory.INSTANCE, mergedCustomNumberFormats.get("x"));
- Map<String, String> mergedAutoImports = tc1.getAutoImports();
+ Map<String, String> mergedAutoImports = tcb1.getAutoImports();
assertEquals("a1.ftl", mergedAutoImports.get("a"));
assertEquals("b2.ftl", mergedAutoImports.get("b"));
assertEquals("c2.ftl", mergedAutoImports.get("c"));
// Empty map merging optimization:
- tc1.merge(new TemplateConfiguration.Builder());
- assertSame(mergedCustomDateFormats, tc1.getCustomDateFormats());
- assertSame(mergedCustomNumberFormats, tc1.getCustomNumberFormats());
+ tcb1.merge(new TemplateConfiguration.Builder().build());
+ assertSame(mergedCustomDateFormats, tcb1.getCustomDateFormats());
+ assertSame(mergedCustomNumberFormats, tcb1.getCustomNumberFormats());
// Empty map merging optimization:
- TemplateConfiguration.Builder tc3 = new TemplateConfiguration.Builder();
- tc3.merge(tc1);
- assertSame(mergedCustomDateFormats, tc3.getCustomDateFormats());
- assertSame(mergedCustomNumberFormats, tc3.getCustomNumberFormats());
+ TemplateConfiguration.Builder tcb3 = new TemplateConfiguration.Builder();
+ tcb3.merge(tcb1.build());
+ assertSame(mergedCustomDateFormats, tcb3.getCustomDateFormats());
+ assertSame(mergedCustomNumberFormats, tcb3.getCustomNumberFormats());
}
@Test
public void testMergeListSettings() throws Exception {
- TemplateConfiguration.Builder tc1 = new TemplateConfiguration.Builder();
- tc1.setAutoIncludes(ImmutableList.of("a.ftl", "x.ftl", "b.ftl"));
+ TemplateConfiguration.Builder tcb1 = new TemplateConfiguration.Builder();
+ tcb1.setAutoIncludes(ImmutableList.of("a.ftl", "x.ftl", "b.ftl"));
- TemplateConfiguration.Builder tc2 = new TemplateConfiguration.Builder();
- tc2.setAutoIncludes(ImmutableList.of("c.ftl", "x.ftl", "d.ftl"));
+ TemplateConfiguration.Builder tcb2 = new TemplateConfiguration.Builder();
+ tcb2.setAutoIncludes(ImmutableList.of("c.ftl", "x.ftl", "d.ftl"));
- tc1.merge(tc2);
+ tcb1.merge(tcb2.build());
- assertEquals(ImmutableList.of("a.ftl", "b.ftl", "c.ftl", "x.ftl", "d.ftl"), tc1.getAutoIncludes());
+ assertEquals(ImmutableList.of("a.ftl", "b.ftl", "c.ftl", "x.ftl", "d.ftl"), tcb1.getAutoIncludes());
}
@Test
public void testMergePriority() throws Exception {
- TemplateConfiguration.Builder tc1 = new TemplateConfiguration.Builder();
- tc1.setDateFormat("1");
- tc1.setTimeFormat("1");
- tc1.setDateTimeFormat("1");
+ TemplateConfiguration.Builder tcb1 = new TemplateConfiguration.Builder();
+ tcb1.setDateFormat("1");
+ tcb1.setTimeFormat("1");
+ tcb1.setDateTimeFormat("1");
- TemplateConfiguration.Builder tc2 = new TemplateConfiguration.Builder();
- tc2.setDateFormat("2");
- tc2.setTimeFormat("2");
+ TemplateConfiguration.Builder tcb2 = new TemplateConfiguration.Builder();
+ tcb2.setDateFormat("2");
+ tcb2.setTimeFormat("2");
- TemplateConfiguration.Builder tc3 = new TemplateConfiguration.Builder();
- tc3.setDateFormat("3");
+ TemplateConfiguration.Builder tcb3 = new TemplateConfiguration.Builder();
+ tcb3.setDateFormat("3");
- tc1.merge(tc2);
- tc1.merge(tc3);
+ tcb1.merge(tcb2.build());
+ tcb1.merge(tcb3.build());
- assertEquals("3", tc1.getDateFormat());
- assertEquals("2", tc1.getTimeFormat());
- assertEquals("1", tc1.getDateTimeFormat());
+ assertEquals("3", tcb1.getDateFormat());
+ assertEquals("2", tcb1.getTimeFormat());
+ assertEquals("1", tcb1.getDateTimeFormat());
}
@Test
@@ -403,18 +403,18 @@ public class TemplateConfigurationTest {
tc1.setCustomAttribute(CA2, "V1");
tc1.setCustomAttribute(CA3, "V1");
- TemplateConfiguration.Builder tc2 = new TemplateConfiguration.Builder();
- tc2.setCustomAttribute("k1", "v2");
- tc2.setCustomAttribute("k2", "v2");
- tc2.setCustomAttribute(CA1, "V2");
- tc2.setCustomAttribute(CA2, "V2");
+ TemplateConfiguration.Builder tcb2 = new TemplateConfiguration.Builder();
+ tcb2.setCustomAttribute("k1", "v2");
+ tcb2.setCustomAttribute("k2", "v2");
+ tcb2.setCustomAttribute(CA1, "V2");
+ tcb2.setCustomAttribute(CA2, "V2");
- TemplateConfiguration.Builder tc3 = new TemplateConfiguration.Builder();
- tc3.setCustomAttribute("k1", "v3");
- tc3.setCustomAttribute(CA1, "V3");
+ TemplateConfiguration.Builder tcb3 = new TemplateConfiguration.Builder();
+ tcb3.setCustomAttribute("k1", "v3");
+ tcb3.setCustomAttribute(CA1, "V3");
- tc1.merge(tc2);
- tc1.merge(tc3);
+ tc1.merge(tcb2.build());
+ tc1.merge(tcb3.build());
assertEquals("v3", tc1.getCustomAttribute("k1"));
assertEquals("v2", tc1.getCustomAttribute("k2"));
@@ -426,51 +426,51 @@ public class TemplateConfigurationTest {
@Test
public void testMergeNullCustomAttributes() throws Exception {
- TemplateConfiguration.Builder tc1 = new TemplateConfiguration.Builder();
- tc1.setCustomAttribute("k1", "v1");
- tc1.setCustomAttribute("k2", "v1");
- tc1.setCustomAttribute(CA1, "V1");
- tc1.setCustomAttribute(CA2,"V1");
-
- assertEquals("v1", tc1.getCustomAttribute("k1"));
- assertEquals("v1", tc1.getCustomAttribute("k2"));
- assertNull("v1", tc1.getCustomAttribute("k3"));
- assertEquals("V1", tc1.getCustomAttribute(CA1));
- assertEquals("V1", tc1.getCustomAttribute(CA2));
- assertNull(tc1.getCustomAttribute(CA3));
-
- TemplateConfiguration.Builder tc2 = new TemplateConfiguration.Builder();
- tc2.setCustomAttribute("k1", "v2");
- tc2.setCustomAttribute("k2", null);
- tc2.setCustomAttribute(CA1, "V2");
- tc2.setCustomAttribute(CA2, null);
-
- TemplateConfiguration.Builder tc3 = new TemplateConfiguration.Builder();
- tc3.setCustomAttribute("k1", null);
- tc2.setCustomAttribute(CA1, null);
-
- tc1.merge(tc2);
- tc1.merge(tc3);
-
- assertNull(tc1.getCustomAttribute("k1"));
- assertNull(tc1.getCustomAttribute("k2"));
- assertNull(tc1.getCustomAttribute("k3"));
- assertNull(tc1.getCustomAttribute(CA1));
- assertNull(tc1.getCustomAttribute(CA2));
- assertNull(tc1.getCustomAttribute(CA3));
-
- TemplateConfiguration.Builder tc4 = new TemplateConfiguration.Builder();
- tc4.setCustomAttribute("k1", "v4");
- tc4.setCustomAttribute(CA1, "V4");
-
- tc1.merge(tc4);
-
- assertEquals("v4", tc1.getCustomAttribute("k1"));
- assertNull(tc1.getCustomAttribute("k2"));
- assertNull(tc1.getCustomAttribute("k3"));
- assertEquals("V4", tc1.getCustomAttribute(CA1));
- assertNull(tc1.getCustomAttribute(CA2));
- assertNull(tc1.getCustomAttribute(CA3));
+ TemplateConfiguration.Builder tcb1 = new TemplateConfiguration.Builder();
+ tcb1.setCustomAttribute("k1", "v1");
+ tcb1.setCustomAttribute("k2", "v1");
+ tcb1.setCustomAttribute(CA1, "V1");
+ tcb1.setCustomAttribute(CA2,"V1");
+
+ assertEquals("v1", tcb1.getCustomAttribute("k1"));
+ assertEquals("v1", tcb1.getCustomAttribute("k2"));
+ assertNull("v1", tcb1.getCustomAttribute("k3"));
+ assertEquals("V1", tcb1.getCustomAttribute(CA1));
+ assertEquals("V1", tcb1.getCustomAttribute(CA2));
+ assertNull(tcb1.getCustomAttribute(CA3));
+
+ TemplateConfiguration.Builder tcb2 = new TemplateConfiguration.Builder();
+ tcb2.setCustomAttribute("k1", "v2");
+ tcb2.setCustomAttribute("k2", null);
+ tcb2.setCustomAttribute(CA1, "V2");
+ tcb2.setCustomAttribute(CA2, null);
+
+ TemplateConfiguration.Builder tcb3 = new TemplateConfiguration.Builder();
+ tcb3.setCustomAttribute("k1", null);
+ tcb2.setCustomAttribute(CA1, null);
+
+ tcb1.merge(tcb2.build());
+ tcb1.merge(tcb3.build());
+
+ assertNull(tcb1.getCustomAttribute("k1"));
+ assertNull(tcb1.getCustomAttribute("k2"));
+ assertNull(tcb1.getCustomAttribute("k3"));
+ assertNull(tcb1.getCustomAttribute(CA1));
+ assertNull(tcb1.getCustomAttribute(CA2));
+ assertNull(tcb1.getCustomAttribute(CA3));
+
+ TemplateConfiguration.Builder tcb4 = new TemplateConfiguration.Builder();
+ tcb4.setCustomAttribute("k1", "v4");
+ tcb4.setCustomAttribute(CA1, "V4");
+
+ tcb1.merge(tcb4.build());
+
+ assertEquals("v4", tcb1.getCustomAttribute("k1"));
+ assertNull(tcb1.getCustomAttribute("k2"));
+ assertNull(tcb1.getCustomAttribute("k3"));
+ assertEquals("V4", tcb1.getCustomAttribute(CA1));
+ assertNull(tcb1.getCustomAttribute(CA2));
+ assertNull(tcb1.getCustomAttribute(CA3));
}
@Test
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eae27088/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 1118e8e..dcf0714 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
@@ -330,7 +330,7 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
private Boolean logTemplateExceptions;
private Map<String, TemplateDateFormatFactory> customDateFormats;
private Map<String, TemplateNumberFormatFactory> customNumberFormats;
- private LinkedHashMap<String, String> autoImports;
+ private Map<String, String> autoImports;
private List<String> autoIncludes;
private Boolean lazyImports;
private Boolean lazyAutoImports;
@@ -517,16 +517,37 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
/**
* Setter pair of {@link #getCustomNumberFormats()}. Note that custom number formats are get through
* {@link #getCustomNumberFormat(String)}, not directly though this {@link Map}, so number formats from
- * {@link ProcessingConfiguration}-s on less specific levels are inherited without you copying them into this
+ * {@link ProcessingConfiguration}-s on less specific levels are inherited without being present in this
* {@link Map}.
*
* @param customNumberFormats
- * Not {@code null}.
+ * Not {@code null}; will be copied (to prevent aliasing effect); keys must conform to format name
+ * syntactical restrictions (see in {@link #getCustomNumberFormats()})
*/
public void setCustomNumberFormats(Map<String, TemplateNumberFormatFactory> customNumberFormats) {
+ setCustomNumberFormats(customNumberFormats, false);
+ }
+
+ /**
+ * @param validatedImmutableUnchanging
+ * {@code true} if we know that the 1st argument is already validated, immutable, and unchanging (means,
+ * won't change later because of aliasing).
+ */
+ void setCustomNumberFormats(Map<String, TemplateNumberFormatFactory> customNumberFormats,
+ boolean validatedImmutableUnchanging) {
_NullArgumentException.check("customNumberFormats", customNumberFormats);
- validateFormatNames(customNumberFormats.keySet());
- this.customNumberFormats = customNumberFormats;
+ if (!validatedImmutableUnchanging) {
+ if (customNumberFormats == this.customNumberFormats) {
+ return;
+ }
+ _CollectionUtil.safeCastMap("customNumberFormats", customNumberFormats,
+ String.class, false,
+ TemplateNumberFormatFactory.class, false);
+ validateFormatNames(customNumberFormats.keySet());
+ this.customNumberFormats = Collections.unmodifiableMap(new HashMap<>(customNumberFormats));
+ } else {
+ this.customNumberFormats = customNumberFormats;
+ }
}
/**
@@ -763,12 +784,40 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
protected abstract Map<String, TemplateDateFormatFactory> getDefaultCustomDateFormats();
/**
- * Setter pair of {@link #getCustomDateFormat(String)}.
+ * Setter pair of {@link #getCustomDateFormat(String)}. Note that custom date formats are get through
+ * {@link #getCustomNumberFormat(String)}, not directly though this {@link Map}, so date formats from
+ * {@link ProcessingConfiguration}-s on less specific levels are inherited without being present in this
+ * {@link Map}.
+ *
+ * @param customDateFormats
+ * Not {@code null}; will be copied (to prevent aliasing effect); keys must conform to format name
+ * syntactical restrictions (see in {@link #getCustomDateFormats()})
*/
public void setCustomDateFormats(Map<String, TemplateDateFormatFactory> customDateFormats) {
+ setCustomDateFormats(customDateFormats, false);
+ }
+
+ /**
+ * @param validatedImmutableUnchanging
+ * {@code true} if we know that the 1st argument is already validated, immutable, and unchanging (means,
+ * won't change later because of aliasing).
+ */
+ void setCustomDateFormats(
+ Map<String, TemplateDateFormatFactory> customDateFormats,
+ boolean validatedImmutableUnchanging) {
_NullArgumentException.check("customDateFormats", customDateFormats);
- validateFormatNames(customDateFormats.keySet());
- this.customDateFormats = customDateFormats;
+ if (!validatedImmutableUnchanging) {
+ if (customDateFormats == this.customDateFormats) {
+ return;
+ }
+ _CollectionUtil.safeCastMap("customDateFormats", customDateFormats,
+ String.class, false,
+ TemplateDateFormatFactory.class, false);
+ validateFormatNames(customDateFormats.keySet());
+ this.customDateFormats = Collections.unmodifiableMap(new HashMap(customDateFormats));
+ } else {
+ this.customDateFormats = customDateFormats;
+ }
}
/**
@@ -1292,15 +1341,31 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
/**
* Setter pair of {@link #getAutoImports()}.
*
- * @param map
+ * @param autoImports
* Maps the namespace variable names to the template names; not {@code null}, and can't contain {@code
* null} keys of values. The content of the {@link Map} is copied into another {@link Map}, to avoid
* aliasing problems. The iteration order of the original {@link Map} entries is kept.
*/
- public void setAutoImports(Map<String, String> map) {
- _NullArgumentException.check("map", map);
- _CollectionUtil.safeCastMap("map", map, String.class, false, String.class, false);
- autoImports = new LinkedHashMap<>(map);
+ public void setAutoImports(Map<String, String> autoImports) {
+ setAutoImports(autoImports, false);
+ }
+
+ /**
+ * @param validatedImmutableUnchanging
+ * {@code true} if we know that the 1st argument is already validated, immutable, and unchanging (means,
+ * won't change later because of aliasing).
+ */
+ void setAutoImports(Map<String, String> autoImports, boolean validatedImmutableUnchanging) {
+ _NullArgumentException.check("autoImports", autoImports);
+ if (!validatedImmutableUnchanging) {
+ if (autoImports == this.autoImports) {
+ return;
+ }
+ _CollectionUtil.safeCastMap("autoImports", autoImports, String.class, false, String.class, false);
+ this.autoImports = new LinkedHashMap<>(autoImports);
+ } else {
+ this.autoImports = autoImports;
+ }
}
/**
@@ -1338,20 +1403,36 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
/**
* Setter pair of {@link #getAutoIncludes()}
*
- * @param templateNames Not {@code null}. The {@link List} will be copied to avoid aliasing problems.
- */
- public void setAutoIncludes(List<String> templateNames) {
- _NullArgumentException.check("templateNames", templateNames);
- _CollectionUtil.safeCastList("templateNames", templateNames, String.class, false);
- Set<String> uniqueItems = new LinkedHashSet<>(templateNames.size() * 4 / 3, 1f);
- for (String templateName : templateNames) {
- if (!uniqueItems.add(templateName)) {
- // Move clashing item at the end of the collection
- uniqueItems.remove(templateName);
- uniqueItems.add(templateName);
+ * @param autoIncludes Not {@code null}. The {@link List} will be copied to avoid aliasing problems.
+ */
+ public void setAutoIncludes(List<String> autoIncludes) {
+ setAutoIncludes(autoIncludes, false);
+ }
+
+ /**
+ * @param validatedImmutableUnchanging
+ * {@code true} if we know that the 1st argument is already validated, immutable, and unchanging (means,
+ * won't change later because of aliasing).
+ */
+ void setAutoIncludes(List<String> autoIncludes, boolean validatedImmutableUnchanging) {
+ _NullArgumentException.check("autoIncludes", autoIncludes);
+ if (!validatedImmutableUnchanging) {
+ if (autoIncludes == this.autoIncludes) {
+ return;
}
+ _CollectionUtil.safeCastList("autoIncludes", autoIncludes, String.class, false);
+ Set<String> uniqueItems = new LinkedHashSet<>(autoIncludes.size() * 4 / 3, 1f);
+ for (String templateName : autoIncludes) {
+ if (!uniqueItems.add(templateName)) {
+ // Move clashing item at the end of the collection
+ uniqueItems.remove(templateName);
+ uniqueItems.add(templateName);
+ }
+ }
+ this.autoIncludes = Collections.<String>unmodifiableList(new ArrayList<>(uniqueItems));
+ } else {
+ this.autoIncludes = autoIncludes;
}
- autoIncludes = Collections.<String>unmodifiableList(new ArrayList<>(uniqueItems));
}
/**
@@ -2056,24 +2137,29 @@ public abstract class MutableProcessingConfiguration<SelfT extends MutableProces
* @param customAttributes Not {@code null}. The {@link Map} is copied to prevent aliasing problems.
*/
public void setCustomAttributes(Map<Object, Object> customAttributes) {
- setCustomAttributesWithoutCopying(new LinkedHashMap<>(customAttributes));
+ setCustomAttributes(customAttributes, false);
}
/**
- * Fluent API equivalent of {@link #setCustomAttributes(Map)}
+ * @param validatedImmutableUnchanging
+ * {@code true} if we know that the 1st argument is already validated, immutable, and unchanging (means,
+ * won't change later because of aliasing).
*/
- public SelfT customAttributes(Map<Object, Object> customAttributes) {
- setCustomAttributes(customAttributes);
- return self();
+ void setCustomAttributes(Map<Object, Object> customAttributes, boolean validatedImmutableUnchanging) {
+ _NullArgumentException.check("customAttributes", customAttributes);
+ if (!validatedImmutableUnchanging) {
+ this.customAttributes = new LinkedHashMap<>(customAttributes); // TODO mutable
+ } else {
+ this.customAttributes = customAttributes;
+ }
}
/**
- * Used internally instead of {@link #setCustomAttributes(Map)} to speed up use cases where we know that there
- * won't be aliasing problems.
+ * Fluent API equivalent of {@link #setCustomAttributes(Map)}
*/
- void setCustomAttributesWithoutCopying(Map<Object, Object> customAttributes) {
- _NullArgumentException.check("customAttributes", customAttributes);
- this.customAttributes = customAttributes;
+ public SelfT customAttributes(Map<Object, Object> customAttributes) {
+ setCustomAttributes(customAttributes);
+ return self();
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eae27088/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
index 6f6b33d..d68ab78 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ProcessingConfiguration.java
@@ -189,8 +189,7 @@ public interface ProcessingConfiguration {
* inheritance. Thus, to get a custom format you shouldn't use this {@link Map} directly, but
* {@link #getCustomNumberFormat(String)}, which will search the format in the inheritance chain.
*
- * @return Never {@code null}. Unless the method was called on a builder class, the returned {@link Map} shouldn't
- * be modified.
+ * @return Never {@code null}; unmodifiable {@link Map}.
*/
Map<String, TemplateNumberFormatFactory> getCustomNumberFormats();
@@ -372,8 +371,7 @@ public interface ProcessingConfiguration {
* inheritance. Thus, to get a custom format you shouldn't use this {@link Map} directly, but {@link
* #getCustomDateFormat(String)}, which will search the format in the inheritance chain.
*
- * @return Never {@code null}. Unless the method was called on a builder class, the returned {@link Map} shouldn't
- * be modified.
+ * @return Never {@code null}; unmodifiable {@link Map}.
*/
Map<String, TemplateDateFormatFactory> getCustomDateFormats();
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eae27088/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 47d21b8..8a6ccc3 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,6 +21,7 @@ package org.apache.freemarker.core;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
@@ -135,6 +136,10 @@ 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;
@@ -149,9 +154,13 @@ public final class TemplateConfiguration implements ParsingAndProcessingConfigur
}
}
mergedM.putAll(m2);
- return mergedM;
+ 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) {
if (list1 == null) return list2;
if (list2 == null) return list1;
@@ -161,7 +170,7 @@ public final class TemplateConfiguration implements ParsingAndProcessingConfigur
ArrayList<String> mergedList = new ArrayList<>(list1.size() + list2.size());
mergedList.addAll(list1);
mergedList.addAll(list2);
- return mergedList;
+ return Collections.unmodifiableList(mergedList);
}
/**
@@ -814,11 +823,12 @@ public final class TemplateConfiguration implements ParsingAndProcessingConfigur
}
/**
- * Set all settings in this {@link Builder} 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.)
+ * Set all settings in this {@link Builder} that were set in the parameter {@link TemplateConfiguration} (or
+ * other {@link ParsingAndProcessingConfiguration}), possibly overwriting the earlier value in this object.
+ * (A setting is said to be set in a {@link ParsingAndProcessingConfiguration} if it was explicitly set via a
+ * setter method, as opposed to be inherited.)
*/
- public void merge(ParsingAndProcessingConfiguration tc) {
+ public void merge(TemplateConfiguration tc) {
if (tc.isAPIBuiltinEnabledSet()) {
setAPIBuiltinEnabled(tc.getAPIBuiltinEnabled());
}
@@ -836,11 +846,14 @@ public final class TemplateConfiguration implements ParsingAndProcessingConfigur
}
if (tc.isCustomDateFormatsSet()) {
setCustomDateFormats(mergeMaps(
- isCustomDateFormatsSet() ? getCustomDateFormats() : null, tc.getCustomDateFormats(), false));
+ isCustomDateFormatsSet() ? getCustomDateFormats() : null, tc.getCustomDateFormats(), false),
+ true
+ );
}
if (tc.isCustomNumberFormatsSet()) {
setCustomNumberFormats(mergeMaps(
- isCustomNumberFormatsSet() ? getCustomNumberFormats() : null, tc.getCustomNumberFormats(), false));
+ isCustomNumberFormatsSet() ? getCustomNumberFormats() : null, tc.getCustomNumberFormats(), false),
+ true);
}
if (tc.isDateFormatSet()) {
setDateFormat(tc.getDateFormat());
@@ -927,10 +940,11 @@ public final class TemplateConfiguration implements ParsingAndProcessingConfigur
}
if (tc.isCustomAttributesSet()) {
- setCustomAttributesWithoutCopying(mergeMaps(
+ setCustomAttributes(mergeMaps(
isCustomAttributesSet() ? getCustomAttributes() : null,
tc.isCustomAttributesSet() ? tc.getCustomAttributes() : null,
- true));
+ true),
+ true);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/eae27088/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 397f951..bd703af 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
@@ -19,10 +19,14 @@
package org.apache.freemarker.core.util;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
-/** Don't use this; used internally by FreeMarker, might changes without notice. */
+/**
+ * Don't use this; used internally by FreeMarker, might changes without notice.
+ * {@link Collection} and {@link Map}-related utilities.
+ */
public class _CollectionUtil {
private _CollectionUtil() { }
@@ -74,12 +78,12 @@ public class _CollectionUtil {
if (key == null) {
if (!allowNullKey) {
throw new IllegalArgumentException(
- (argName != null ? "Invalid value for argument \"" + argName + "\"" : "")
+ (argName != null ? "Invalid value for argument \"" + argName + "\": " : "")
+ "The Map contains null key");
}
} else {
throw new IllegalArgumentException(
- (argName != null ? "Invalid value for argument \"" + argName + "\"" : "")
+ (argName != null ? "Invalid value for argument \"" + argName + "\": " : "")
+ "The Map contains a key that's not instance of " + keyClass.getName() +
"; its class is " + key.getClass().getName() + ".");
}