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/10 17:59:26 UTC

incubator-freemarker git commit: Added protected getImpliedRegisteredCustomOutputFormats() as well, and some cleanup around the customOutputFormats setting.

Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 164f292ef -> bd1c4c40d


Added protected getImpliedRegisteredCustomOutputFormats() as well, and some cleanup around the customOutputFormats setting.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/bd1c4c40
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/bd1c4c40
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/bd1c4c40

Branch: refs/heads/3
Commit: bd1c4c40d828a87cd23733e948ec2edcb96b0444
Parents: 164f292
Author: ddekany <dd...@apache.org>
Authored: Sat Jun 10 19:58:49 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Jun 10 19:58:49 2017 +0200

----------------------------------------------------------------------
 .../freemarker/core/ConfigurationTest.java      | 33 ++++++-
 .../userpkg/NameClashingDummyOutputFormat.java  | 65 +++++++++++++
 .../apache/freemarker/core/Configuration.java   | 97 +++++++++++++-------
 .../freemarker/core/TopLevelConfiguration.java  | 33 +++++++
 4 files changed, 191 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bd1c4c40/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 7cf205f..ae9bd74 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
@@ -76,6 +76,8 @@ import org.apache.freemarker.core.userpkg.DummyOutputFormat;
 import org.apache.freemarker.core.userpkg.EpochMillisDivTemplateDateFormatFactory;
 import org.apache.freemarker.core.userpkg.EpochMillisTemplateDateFormatFactory;
 import org.apache.freemarker.core.userpkg.HexTemplateNumberFormatFactory;
+import org.apache.freemarker.core.userpkg.NameClashingDummyOutputFormat;
+import org.apache.freemarker.core.userpkg.SeldomEscapedOutputFormat;
 import org.apache.freemarker.core.util._CollectionUtil;
 import org.apache.freemarker.core.util._DateUtil;
 import org.apache.freemarker.core.util._NullArgumentException;
@@ -1457,7 +1459,8 @@ public class ConfigurationTest extends TestCase {
     }
 
     @Test
-    public void testImpliedSettingValues() throws IOException, TemplateConfigurationFactoryException {
+    public void testImpliedSettingValues()
+            throws IOException, TemplateConfigurationFactoryException, UnregisteredOutputFormatException {
         Configuration cfg = new ImpliedSettingValuesTestBuilder().build();
 
         assertEquals("Y,N", cfg.getTemplateConfigurations().get("t.yn", null).getBooleanFormat());
@@ -1466,10 +1469,16 @@ public class ConfigurationTest extends TestCase {
         assertEquals(ImmutableMap.of("lib", "lib.ftl"), cfg.getAutoImports());
         assertEquals(ImmutableList.of("inc.ftl"), cfg.getAutoIncludes());
         assertEquals(ImmutableMap.of("v", 1), cfg.getSharedVariables());
+        assertEquals(ImmutableList.of(CustomHTMLOutputFormat.INSTANCE, DummyOutputFormat.INSTANCE),
+                cfg.getRegisteredCustomOutputFormats());
+        assertSame(CustomHTMLOutputFormat.INSTANCE, cfg.getOutputFormat("HTML"));
+        assertSame(DummyOutputFormat.INSTANCE, cfg.getOutputFormat("dummy"));
+        assertSame(XMLOutputFormat.INSTANCE, cfg.getOutputFormat("XML"));
     }
 
     @Test
-    public void testImpliedSettingValues2() throws IOException, TemplateConfigurationFactoryException {
+    public void testImpliedSettingValues2()
+            throws IOException, TemplateConfigurationFactoryException, UnregisteredOutputFormatException {
         Configuration cfg = new ImpliedSettingValuesTestBuilder()
                 .templateConfigurations(
                         new ConditionalTemplateConfigurationFactory(
@@ -1482,6 +1491,8 @@ public class ConfigurationTest extends TestCase {
                 .autoImports(ImmutableMap.of("lib2", "lib2.ftl"))
                 .autoIncludes(ImmutableList.of("inc2.ftl"))
                 .sharedVariables(ImmutableMap.of("v2", 2))
+                .registeredCustomOutputFormats(
+                        SeldomEscapedOutputFormat.INSTANCE, NameClashingDummyOutputFormat.INSTANCE)
                 .build();
 
         TemplateConfigurationFactory tcf = cfg.getTemplateConfigurations();
@@ -1499,6 +1510,17 @@ public class ConfigurationTest extends TestCase {
         assertEquals(ImmutableList.of("inc.ftl", "inc2.ftl"), cfg.getAutoIncludes());
 
         assertEquals(ImmutableMap.of("v", 1, "v2", 2), cfg.getSharedVariables());
+
+        assertEquals(
+                ImmutableList.of(
+                        CustomHTMLOutputFormat.INSTANCE,
+                        SeldomEscapedOutputFormat.INSTANCE,
+                        NameClashingDummyOutputFormat.INSTANCE),
+                cfg.getRegisteredCustomOutputFormats());
+        assertSame(CustomHTMLOutputFormat.INSTANCE, cfg.getOutputFormat("HTML"));
+        assertSame(NameClashingDummyOutputFormat.INSTANCE, cfg.getOutputFormat("dummy"));
+        assertSame(SeldomEscapedOutputFormat.INSTANCE, cfg.getOutputFormat("seldomEscaped"));
+        assertSame(XMLOutputFormat.INSTANCE, cfg.getOutputFormat("XML"));
     }
 
     @SuppressWarnings("boxing")
@@ -1568,9 +1590,14 @@ public class ConfigurationTest extends TestCase {
         }
 
         @Override
-        protected Map<String, Object> getImplicitSharedVariables() {
+        protected Map<String, Object> getImpliedSharedVariables() {
             return ImmutableMap.<String, Object>of("v", 1);
         }
+
+        @Override
+        protected Collection<OutputFormat> getImpliedRegisteredCustomOutputFormats() {
+            return ImmutableList.<OutputFormat>of(CustomHTMLOutputFormat.INSTANCE, DummyOutputFormat.INSTANCE);
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bd1c4c40/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NameClashingDummyOutputFormat.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NameClashingDummyOutputFormat.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NameClashingDummyOutputFormat.java
new file mode 100644
index 0000000..dd26156
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/userpkg/NameClashingDummyOutputFormat.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core.userpkg;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.outputformat.CommonMarkupOutputFormat;
+
+public class NameClashingDummyOutputFormat extends CommonMarkupOutputFormat<TemplateDummyOutputModel> {
+
+    public static final NameClashingDummyOutputFormat INSTANCE = new NameClashingDummyOutputFormat();
+
+    private NameClashingDummyOutputFormat() {
+        // hide
+    }
+
+    @Override
+    public String getName() {
+        return "dummy";
+    }
+
+    @Override
+    public String getMimeType() {
+        return "text/dummy";
+    }
+
+    @Override
+    public void output(String textToEsc, Writer out) throws IOException, TemplateModelException {
+        out.write(escapePlainText(textToEsc));
+    }
+
+    @Override
+    public String escapePlainText(String plainTextContent) {
+        return plainTextContent.replaceAll("(\\.|\\\\)", "\\\\$1");
+    }
+
+    @Override
+    public boolean isLegacyBuiltInBypassed(String builtInName) {
+        return false;
+    }
+
+    @Override
+    protected TemplateDummyOutputModel newTemplateMarkupOutputModel(String plainTextContent, String markupContent) {
+        return new TemplateDummyOutputModel(plainTextContent, markupContent);
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bd1c4c40/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 93bd1ff..3b96857 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
@@ -26,9 +26,11 @@ import java.io.InputStream;
 import java.io.Serializable;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -302,12 +304,36 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
         incompatibleImprovements = builder.getIncompatibleImprovements();
 
         {
-            Collection<OutputFormat> registeredCustomOutputFormats = builder.getRegisteredCustomOutputFormats();
+            final Collection<OutputFormat> regCustOutputFormats;
+            {
+                Collection<OutputFormat> directRegCustOutputFormats = builder.getRegisteredCustomOutputFormats();
+                Collection<OutputFormat> impliedRegCustOutputFormats =
+                        builder.getImpliedRegisteredCustomOutputFormats();
+                if (impliedRegCustOutputFormats.isEmpty()) {
+                    regCustOutputFormats = directRegCustOutputFormats;
+                } else if (directRegCustOutputFormats.isEmpty()) {
+                    regCustOutputFormats = impliedRegCustOutputFormats;
+                } else {
+                    List<OutputFormat> mergedOutputFormats = new ArrayList<>(
+                            impliedRegCustOutputFormats.size() + directRegCustOutputFormats.size());
+                    HashSet<String> directNames = new HashSet<>(directRegCustOutputFormats.size() * 4 / 3 + 1, .75f);
+                    for (OutputFormat directRegCustOutputFormat : directRegCustOutputFormats) {
+                        directNames.add(directRegCustOutputFormat.getName());
+                    }
+                    for (OutputFormat impliedRegCustOutputFormat : impliedRegCustOutputFormats) {
+                        if (!directNames.contains(impliedRegCustOutputFormat.getName())) {
+                            mergedOutputFormats.add(impliedRegCustOutputFormat);
+                        }
+                    }
+                    mergedOutputFormats.addAll(directRegCustOutputFormats);
+                    regCustOutputFormats = Collections.unmodifiableList(mergedOutputFormats);
+                }
+            }
 
-            _NullArgumentException.check(registeredCustomOutputFormats);
+            _NullArgumentException.check(regCustOutputFormats);
             Map<String, OutputFormat> registeredCustomOutputFormatsByName = new LinkedHashMap<>(
-                    registeredCustomOutputFormats.size() * 4 / 3, 1f);
-            for (OutputFormat outputFormat : registeredCustomOutputFormats) {
+                    regCustOutputFormats.size() * 4 / 3, 1f);
+            for (OutputFormat outputFormat : regCustOutputFormats) {
                 String name = outputFormat.getName();
                 if (name.equals(UndefinedOutputFormat.INSTANCE.getName())) {
                     throw new InvalidSettingValueException(
@@ -367,14 +393,14 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
 
             this.registeredCustomOutputFormatsByName = registeredCustomOutputFormatsByName;
             this.registeredCustomOutputFormats = Collections.unmodifiableList(
-                    new ArrayList<> (registeredCustomOutputFormats));
+                    new ArrayList<> (regCustOutputFormats));
         }
 
         ObjectWrapper objectWrapper = builder.getObjectWrapper();
 
         {
             Map<String, Object> sharedVariables = _CollectionUtil.mergeImmutableMaps(builder
-                    .getImplicitSharedVariables(), builder.getSharedVariables(), false);
+                    .getImpliedSharedVariables(), builder.getSharedVariables(), false);
 
             HashMap<String, TemplateModel> wrappedSharedVariables = new HashMap<>(
                     sharedVariables.size() * 4 / 3 + 1, 0.75f);
@@ -803,32 +829,19 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
         return (MarkupOutputFormat) of;
     }
     
-    /**
-     * The custom output formats that can be referred by their unique name ({@link OutputFormat#getName()}) from
-     * templates. Names are also used to look up the {@link OutputFormat} for standard file extensions; see them at
-     * {@link #getRecognizeStandardFileExtensions()}. Each must be different and has a unique name
-     * ({@link OutputFormat#getName()}) within this collection.
-     *
-     * <p>
-     * When there's a clash between a custom output format name and a standard output format name, the custom format
-     * will win, thus you can override the meaning of standard output format names. Except, it's not allowed to override
-     * {@link UndefinedOutputFormat} and {@link PlainTextOutputFormat}.
-     *
-     * <p>
-     * The default value is an empty collection.
-     *
-     * @throws IllegalArgumentException
-     *             When multiple different {@link OutputFormat}-s have the same name in the parameter collection. When
-     *             the same {@link OutputFormat} object occurs for multiple times in the collection. If an
-     *             {@link OutputFormat} name is 0 long. If an {@link OutputFormat} name doesn't start with letter or
-     *             digit. If an {@link OutputFormat} name contains {@code '+'} or <code>'{'</code> or <code>'}'</code>.
-     *             If an {@link OutputFormat} name equals to {@link UndefinedOutputFormat#getName()} or
-     *             {@link PlainTextOutputFormat#getName()}.
-     */
     public Collection<OutputFormat> getRegisteredCustomOutputFormats() {
         return registeredCustomOutputFormats;
     }
 
+    /**
+     * Always {@code true} in {@link Configuration}-s; even if this setting wasn't set in the builder, it gets a default
+     * value in the {@link Configuration}.
+     */
+    @Override
+    public boolean isRegisteredCustomOutputFormatsSet() {
+        return true;
+    }
+
     @Override
     public boolean getRecognizeStandardFileExtensions() {
         return recognizeStandardFileExtensions == null
@@ -2477,9 +2490,6 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
                     : getDefaultRegisteredCustomOutputFormats();
         }
 
-        /**
-         * Tells if this setting was explicitly set (if not, the default value of the setting will be used).
-         */
         public boolean isRegisteredCustomOutputFormatsSet() {
             return registeredCustomOutputFormats != null;
         }
@@ -2489,11 +2499,23 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
         }
 
         /**
+         * The imports that will be added to the built {@link Configuration} before the ones coming from
+         * {@link #getRegisteredCustomOutputFormats()}. When overriding this method, always consider adding to the
+         * return value of the super method, rather than replacing it.
+         *
+         * @return Immutable {@link Collection}; not {@code null}
+         */
+        protected Collection<OutputFormat> getImpliedRegisteredCustomOutputFormats() {
+            return Collections.emptyList();
+        }
+
+        /**
          * Setter pair of {@link Configuration#getRegisteredCustomOutputFormats()}.
          */
         public void setRegisteredCustomOutputFormats(Collection<OutputFormat> registeredCustomOutputFormats) {
             _NullArgumentException.check("registeredCustomOutputFormats", registeredCustomOutputFormats);
-            this.registeredCustomOutputFormats = registeredCustomOutputFormats;
+            this.registeredCustomOutputFormats = Collections.unmodifiableCollection(
+                    new ArrayList<>(registeredCustomOutputFormats));
         }
 
         /**
@@ -2505,6 +2527,13 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
         }
 
         /**
+         * Varargs overload if {@link #registeredCustomOutputFormats(Collection)}.
+         */
+        public SelfT registeredCustomOutputFormats(OutputFormat... registeredCustomOutputFormats) {
+            return registeredCustomOutputFormats(Arrays.asList(registeredCustomOutputFormats));
+        }
+
+        /**
          * Resets this setting to its initial state, as if it was never set.
          */
         public void unsetRegisteredCustomOutputFormats() {
@@ -2534,7 +2563,7 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
         /**
          * The {@link Map} to use as shared variables if {@link #isSharedVariablesSet()} is {@code false}.
          *
-         * @see #getImplicitSharedVariables()
+         * @see #getImpliedSharedVariables()
          */
         protected Map<String, Object> getDefaultSharedVariables() {
             return Collections.emptyMap();
@@ -2559,7 +2588,7 @@ public final class Configuration implements TopLevelConfiguration, CustomStateSc
          *
          * @return Immutable {@link Map}; not {@code null}
          */
-        protected Map<String, Object> getImplicitSharedVariables() {
+        protected Map<String, Object> getImpliedSharedVariables() {
             return DEFAULT_SHARED_VARIABLES;
         }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/bd1c4c40/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java
index fed778e..66d3495 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java
@@ -19,9 +19,13 @@
 
 package org.apache.freemarker.core;
 
+import java.util.Collection;
 import java.util.Map;
 
 import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.outputformat.OutputFormat;
+import org.apache.freemarker.core.outputformat.impl.PlainTextOutputFormat;
+import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat;
 import org.apache.freemarker.core.templateresolver.CacheStorage;
 import org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory;
 import org.apache.freemarker.core.templateresolver.TemplateLoader;
@@ -237,4 +241,33 @@ public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration
      */
     boolean isSharedVariablesSet();
 
+    /**
+     * The custom output formats that can be referred by their unique name ({@link OutputFormat#getName()}) from
+     * templates. Names are also used to look up the {@link OutputFormat} for standard file extensions; see them at
+     * {@link #getRecognizeStandardFileExtensions()}. Each must be different and has a unique name
+     * ({@link OutputFormat#getName()}) within this collection.
+     *
+     * <p>
+     * When there's a clash between a custom output format name and a standard output format name, the custom format
+     * will win, thus you can override the meaning of standard output format names. Except, it's not allowed to override
+     * {@link UndefinedOutputFormat} and {@link PlainTextOutputFormat}.
+     *
+     * <p>
+     * The default value is an empty collection.
+     *
+     * @throws IllegalArgumentException
+     *             When multiple different {@link OutputFormat}-s have the same name in the parameter collection. When
+     *             the same {@link OutputFormat} object occurs for multiple times in the collection. If an
+     *             {@link OutputFormat} name is 0 long. If an {@link OutputFormat} name doesn't start with letter or
+     *             digit. If an {@link OutputFormat} name contains {@code '+'} or <code>'{'</code> or <code>'}'</code>.
+     *             If an {@link OutputFormat} name equals to {@link UndefinedOutputFormat#getName()} or
+     *             {@link PlainTextOutputFormat#getName()}.
+     */
+    Collection<OutputFormat> getRegisteredCustomOutputFormats();
+
+    /**
+     * Tells if this setting was explicitly set (if not, the default value of the setting will be used).
+     */
+    boolean isRegisteredCustomOutputFormatsSet();
+
 }