You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by sg...@apache.org on 2021/01/04 23:02:45 UTC

[freemarker-generator] branch FREEMARKER-161 created (now c670c73)

This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a change to branch FREEMARKER-161
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git.


      at c670c73  FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI

This branch includes the following new commits:

     new e73f23d  FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI
     new 68df50c  Merge branch 'master' into FREEMARKER-161
     new 045dc66  FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI
     new 5f89acd  FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI
     new d7d9639  FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI
     new c670c73  FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[freemarker-generator] 06/06: FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI

Posted by sg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a commit to branch FREEMARKER-161
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git

commit c670c73ab9df38d2d2bc13901a131fdbb3b4f33b
Author: Siegfried Goeschl <si...@gmail.com>
AuthorDate: Tue Jan 5 00:01:58 2021 +0100

    FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI
---
 .../generator/base/output/OutputGenerator.java     |  79 +++++++
 .../base/output/OutputGeneratorBuilder.java        |  75 ++++++
 .../template/TemplateTransformationsBuilder.java   |  20 +-
 .../freemarker/generator/base/util/MapBuilder.java |   8 +
 .../TemplateTransformationsBuilderTest.java        |  18 +-
 freemarker-generator-cli/CHANGELOG.md              |   2 +
 .../org/apache/freemarker/generator/cli/Main.java  | 135 ++++-------
 .../cli/config/OutputGeneratorsSupplier.java       | 107 +++++++++
 .../freemarker/generator/cli/config/Settings.java  | 254 ++++++---------------
 .../freemarker/generator/cli/config/Suppliers.java |  31 +--
 .../generator/cli/picocli/DataModelDefinition.java |  30 +++
 .../cli/picocli/DataSourceDefinition.java          |  28 +++
 .../cli/picocli/DataSourceFilterDefinition.java    |  30 +++
 .../cli/picocli/OutputGeneratorDefinition.java     | 117 ++++++++++
 .../cli/picocli/TemplateOutputDefinition.java      |  31 +++
 .../cli/picocli/TemplateSourceDefinition.java      |  32 +++
 .../picocli/TemplateSourceFilterDefinition.java    |  31 +++
 .../generator/cli/task/FreeMarkerTask.java         | 126 +++-------
 .../freemarker/generator/cli/PicocliTest.java      |  95 ++++++--
 .../generator/cli/config/CompositeGroupDemo.java   |  22 +-
 .../cli/config/ConfigurationSupplierTest.java      |   3 +-
 .../generator/cli/config/SettingsTest.java         |  81 +++----
 22 files changed, 871 insertions(+), 484 deletions(-)

diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/output/OutputGenerator.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/output/OutputGenerator.java
new file mode 100644
index 0000000..a9cfc37
--- /dev/null
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/output/OutputGenerator.java
@@ -0,0 +1,79 @@
+/*
+ * 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.generator.base.output;
+
+import org.apache.freemarker.generator.base.datasource.DataSource;
+import org.apache.freemarker.generator.base.template.TemplateOutput;
+import org.apache.freemarker.generator.base.template.TemplateSource;
+
+import java.util.List;
+import java.util.Map;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Information about loading templates and writing their output.
+ */
+public class OutputGenerator {
+
+    /** Source of template */
+    private final TemplateSource templateSource;
+
+    /** Output of template */
+    private final TemplateOutput templateOutput;
+
+    /** Data sources used for the transformation */
+    private final List<DataSource> dataSources;
+
+    /** Data sources used for the transformation */
+    private final Map<String, Object> variables;
+
+    public OutputGenerator(
+            TemplateSource templateSource,
+            TemplateOutput templateOutput,
+            List<DataSource> dataSources,
+            Map<String, Object> variables) {
+        this.templateSource = requireNonNull(templateSource);
+        this.templateOutput = requireNonNull(templateOutput);
+        this.dataSources = requireNonNull(dataSources);
+        this.variables = requireNonNull(variables);
+    }
+
+    public TemplateSource getTemplateSource() {
+        return templateSource;
+    }
+
+    public TemplateOutput getTemplateOutput() {
+        return templateOutput;
+    }
+
+    public List<DataSource> getDataSources() {
+        return dataSources;
+    }
+
+    public Map<String, Object> getVariables() {
+        return variables;
+    }
+
+    @Override
+    public String toString() {
+        return "OutputGenerator{" +
+                "templateSource=" + templateSource +
+                ", templateOutput=" + templateOutput +
+                '}';
+    }
+}
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/output/OutputGeneratorBuilder.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/output/OutputGeneratorBuilder.java
new file mode 100644
index 0000000..5f272c7
--- /dev/null
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/output/OutputGeneratorBuilder.java
@@ -0,0 +1,75 @@
+/*
+ * 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.generator.base.output;
+
+import org.apache.freemarker.generator.base.datasource.DataSource;
+import org.apache.freemarker.generator.base.template.TemplateSource;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provide the logic to define multiple transformations from the user input.
+ */
+public class OutputGeneratorBuilder {
+
+    /** Interactive template */
+    private TemplateSource interactiveTemplate;
+
+    /** List of templates and/or template directories to be rendered */
+    private String templateName;
+
+    /** Optional include patterns for resolving source templates or template directories */
+    private final List<String> includes;
+
+    /** Optional exclude patterns for resolving source templates or template directories */
+    private final List<String> excludes;
+
+    /** Optional output file or directory */
+    private String output;
+
+    /** Optional user-supplied writer */
+    private Writer writer;
+
+    /** Data sources used for the transformation */
+    private List<DataSource> dataSources;
+
+    /** Data sources used for the transformation */
+    private Map<String, Object> variables;
+
+    private OutputGeneratorBuilder() {
+        this.templateName = null;
+        this.includes = new ArrayList<>();
+        this.excludes = new ArrayList<>();
+        this.output = null;
+        this.writer = null;
+        this.dataSources = new ArrayList<>();
+        this.variables = new HashMap<>();
+    }
+
+    public static OutputGeneratorBuilder builder() {
+        return new OutputGeneratorBuilder();
+    }
+
+    public List<OutputGenerator> build() {
+        final List<OutputGenerator> result = new ArrayList<>();
+        return result;
+    }
+}
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java
index e352173..5561cb7 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java
@@ -23,13 +23,16 @@ import org.apache.freemarker.generator.base.file.RecursiveFileSupplier;
 import org.apache.freemarker.generator.base.util.StringUtils;
 import org.apache.freemarker.generator.base.util.Validate;
 
+import java.io.BufferedWriter;
 import java.io.File;
+import java.io.OutputStreamWriter;
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Collections.singletonList;
 
 /**
@@ -54,14 +57,14 @@ public class TemplateTransformationsBuilder {
     private final List<String> outputs;
 
     /** Optional user-supplied writer */
-    private Writer writer;
+    private Writer userSuppliedWriter;
 
     private TemplateTransformationsBuilder() {
         this.templateSources = new ArrayList<>();
         this.includes = new ArrayList<>();
         this.excludes = new ArrayList<>();
         this.outputs = new ArrayList<>();
-        this.writer = null;
+        this.userSuppliedWriter = null;
     }
 
     public static TemplateTransformationsBuilder builder() {
@@ -134,13 +137,13 @@ public class TemplateTransformationsBuilder {
         return this;
     }
 
-    public TemplateTransformationsBuilder setWriter(Writer writer) {
-        this.writer = writer;
+    public TemplateTransformationsBuilder setUserSuppliedWriter(Writer userSuppliedWriter) {
+        this.userSuppliedWriter = userSuppliedWriter;
         return this;
     }
 
     private void validate() {
-        Validate.isTrue(interactiveTemplate != null || !templateSources.isEmpty(), "Interactive template does not support multiple sources");
+        // Validate.isTrue(interactiveTemplate != null || !templateSources.isEmpty(), "Interactive template does not support multiple sources");
         Validate.isTrue(interactiveTemplate == null || templateSources.isEmpty(), "No template was provided");
     }
 
@@ -212,10 +215,13 @@ public class TemplateTransformationsBuilder {
     }
 
     private TemplateOutput templateOutput(File templateOutputFile) {
-        if (writer == null && templateOutputFile != null) {
+        if (userSuppliedWriter != null) {
+            return TemplateOutput.fromWriter(userSuppliedWriter);
+        } else if (templateOutputFile != null) {
             return TemplateOutput.fromFile(templateOutputFile);
         } else {
-            return TemplateOutput.fromWriter(writer);
+            // @TODO FREEMARKER-161 sgoeschl Shall we close the writer or use "userSuppliedWriter"?
+            return TemplateOutput.fromWriter(new BufferedWriter(new OutputStreamWriter(System.out, UTF_8)));
         }
     }
 
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapBuilder.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapBuilder.java
index 60daa80..9b71d22 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapBuilder.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapBuilder.java
@@ -18,6 +18,7 @@ package org.apache.freemarker.generator.base.util;
 
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 
 public class MapBuilder {
@@ -50,4 +51,11 @@ public class MapBuilder {
 
         return map;
     }
+
+    public static Map<String, Object> merge(List<Map<String, Object>> maps) {
+        final Map<String, Object> result = new HashMap<>();
+        maps.forEach(result::putAll);
+        return result;
+    }
+
 }
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java
index 66d3c9a..42e933e 100644
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java
+++ b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java
@@ -54,7 +54,7 @@ public class TemplateTransformationsBuilderTest {
     public void shouldCreateFromInteractiveTemplate() {
         final List<TemplateTransformation> transformations = builder()
                 .setInteractiveTemplate("Hello World")
-                .setWriter(stdoutWriter())
+                .setUserSuppliedWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -77,7 +77,7 @@ public class TemplateTransformationsBuilderTest {
         builder()
                 .setInteractiveTemplate("Hello World")
                 .addTemplateSource(ANY_TEMPLATE_FILE_NAME)
-                .setWriter(stdoutWriter())
+                .setUserSuppliedWriter(stdoutWriter())
                 .build();
     }
 
@@ -87,7 +87,7 @@ public class TemplateTransformationsBuilderTest {
     public void shouldCreateFromTemplateFile() {
         final List<TemplateTransformation> transformations = builder()
                 .addTemplateSource(ANY_TEMPLATE_FILE_NAME)
-                .setWriter(stdoutWriter())
+                .setUserSuppliedWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -125,7 +125,7 @@ public class TemplateTransformationsBuilderTest {
     public void shouldCreateFromTemplatePath() {
         final List<TemplateTransformation> transformations = builder()
                 .addTemplateSource(ANY_TEMPLATE_PATH)
-                .setWriter(stdoutWriter())
+                .setUserSuppliedWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -149,7 +149,7 @@ public class TemplateTransformationsBuilderTest {
     public void shouldCreateFromTemplateDirectory() {
         final List<TemplateTransformation> transformations = builder()
                 .addTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
-                .setWriter(stdoutWriter())
+                .setUserSuppliedWriter(stdoutWriter())
                 .build();
 
         assertEquals(2, transformations.size());
@@ -174,7 +174,7 @@ public class TemplateTransformationsBuilderTest {
         final List<TemplateTransformation> transformations = builder()
                 .addTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
                 .addInclude("*.properties")
-                .setWriter(stdoutWriter())
+                .setUserSuppliedWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -186,7 +186,7 @@ public class TemplateTransformationsBuilderTest {
         final List<TemplateTransformation> transformations = builder()
                 .addTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
                 .addExclude("*.ftl")
-                .setWriter(stdoutWriter())
+                .setUserSuppliedWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -199,7 +199,7 @@ public class TemplateTransformationsBuilderTest {
     public void shouldCreateFromTemplateUrl() {
         final List<TemplateTransformation> transformations = builder()
                 .addTemplateSource(ANY_TEMPLATE_URL)
-                .setWriter(stdoutWriter())
+                .setUserSuppliedWriter(stdoutWriter())
                 .build();
 
         final TemplateSource templateSource = transformations.get(0).getTemplateSource();
@@ -218,7 +218,7 @@ public class TemplateTransformationsBuilderTest {
     public void shouldCreateFromTemplateEnvironmentVariable() {
         final List<TemplateTransformation> transformations = builder()
                 .addTemplateSource(ANY_ENV_URI)
-                .setWriter(stdoutWriter())
+                .setUserSuppliedWriter(stdoutWriter())
                 .build();
 
         final TemplateSource templateSource = transformations.get(0).getTemplateSource();
diff --git a/freemarker-generator-cli/CHANGELOG.md b/freemarker-generator-cli/CHANGELOG.md
index c6abcd7..6565367 100644
--- a/freemarker-generator-cli/CHANGELOG.md
+++ b/freemarker-generator-cli/CHANGELOG.md
@@ -19,6 +19,7 @@ All notable changes to this project will be documented in this file. We try to a
 * [FREEMARKER-129] Migrate `freemarker-cli` into `freemarker-generator` project (see [https://github.com/sgoeschl/freemarker-cli](https://github.com/sgoeschl/freemarker-cli))
 
 ### Changed
+* [FREEMARKER-161] Allow multiple transformations on the CLI
 * [FREEMARKER-155] Migrate the FTL code to terser dotter form 
 * [FREEMARKER-153] Packaged templates are now prefixed with `freemarker-generator`, e.g. `freemarker-generator/info.ftl`
 * [FREEMARKER-153] Renamed `--basedir` command line option to `--template-dir`
@@ -61,6 +62,7 @@ All notable changes to this project will be documented in this file. We try to a
 [FREEMARKER-151]: https://issues.apache.org/jira/browse/FREEMARKER-151
 [FREEMARKER-153]: https://issues.apache.org/jira/browse/FREEMARKER-153
 [FREEMARKER-155]: https://issues.apache.org/jira/browse/FREEMARKER-155
+[FREEMARKER-161]: https://issues.apache.org/jira/browse/FREEMARKER-161
 [FREEMARKER-163]: https://issues.apache.org/jira/browse/FREEMARKER-163
 [FREEMARKER-164]: https://issues.apache.org/jira/browse/FREEMARKER-164
 [FREEMARKER-168]: https://issues.apache.org/jira/browse/FREEMARKER-168
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/Main.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/Main.java
index 4869909..b737357 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/Main.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/Main.java
@@ -20,16 +20,16 @@ import org.apache.freemarker.generator.base.FreeMarkerConstants.Configuration;
 import org.apache.freemarker.generator.base.FreeMarkerConstants.SystemProperties;
 import org.apache.freemarker.generator.base.parameter.ParameterModelSupplier;
 import org.apache.freemarker.generator.base.util.ClosableUtils;
-import org.apache.freemarker.generator.base.util.ListUtils;
+import org.apache.freemarker.generator.base.util.MapBuilder;
 import org.apache.freemarker.generator.cli.config.Settings;
 import org.apache.freemarker.generator.cli.picocli.GitVersionProvider;
+import org.apache.freemarker.generator.cli.picocli.OutputGeneratorDefinition;
 import org.apache.freemarker.generator.cli.task.FreeMarkerTask;
 import picocli.CommandLine;
 import picocli.CommandLine.ArgGroup;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Model.CommandSpec;
 import picocli.CommandLine.Option;
-import picocli.CommandLine.ParameterException;
 import picocli.CommandLine.Parameters;
 import picocli.CommandLine.Spec;
 
@@ -38,35 +38,35 @@ import java.io.File;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Properties;
 import java.util.concurrent.Callable;
-import java.util.stream.Collectors;
 import java.util.stream.IntStream;
-import java.util.stream.Stream;
 
+import static java.util.Arrays.asList;
 import static java.util.Objects.requireNonNull;
 import static org.apache.freemarker.generator.base.util.StringUtils.isEmpty;
 import static org.apache.freemarker.generator.base.util.StringUtils.isNotEmpty;
+import static org.apache.freemarker.generator.cli.config.Suppliers.configurationSupplier;
+import static org.apache.freemarker.generator.cli.config.Suppliers.outputGeneratorsSupplier;
 import static org.apache.freemarker.generator.cli.config.Suppliers.propertiesSupplier;
 import static org.apache.freemarker.generator.cli.config.Suppliers.templateDirectorySupplier;
+import static org.apache.freemarker.generator.cli.config.Suppliers.toolsSupplier;
 
 @Command(description = "Apache FreeMarker Generator", name = "freemarker-generator", mixinStandardHelpOptions = true, versionProvider = GitVersionProvider.class)
 public class Main implements Callable<Integer> {
 
-    @ArgGroup(multiplicity = "1")
-    TemplateSourceOptions templateSourceOptions;
+    @ArgGroup(exclusive = false, multiplicity = "1..*")
+    List<OutputGeneratorDefinition> outputGeneratorDefinitions;
 
-    public static final class TemplateSourceOptions {
-        @Option(names = { "-t", "--template" }, description = "templates to process")
-        public List<String> templates;
+    @Option(names = { "--data-source-include" }, description = "data source include pattern")
+    public String dataSourceIncludePattern;
 
-        @Option(names = { "-i", "--interactive" }, description = "interactive template to process")
-        public String interactiveTemplate;
-    }
+    @Option(names = { "--data-source-exclude" }, description = "data source exclude pattern")
+    public String dataSourceExcludePattern;
 
     @Option(names = { "-D", "--system-property" }, description = "set system property")
     Properties systemProperties;
@@ -77,27 +77,12 @@ public class Main implements Callable<Integer> {
     @Option(names = { "-l", "--locale" }, description = "locale being used for the output, e.g. 'en_US'")
     String locale;
 
-    @Option(names = { "-m", "--data-model" }, description = "data model used for rendering")
-    List<String> dataModels;
-
-    @Option(names = { "-o", "--output" }, description = "output files or directories")
-    List<String> outputs;
-
     @Option(names = { "-P", "--param" }, description = "set parameter")
     Map<String, String> parameters;
 
-    @Option(names = { "-s", "--data-source" }, description = "data source used for rendering")
-    List<String> dataSources;
-
     @Option(names = { "--config" }, description = "FreeMarker Generator configuration file")
     String configFile;
 
-    @Option(names = { "--data-source-include" }, description = "file include pattern for data sources")
-    String dataSourceIncludePattern;
-
-    @Option(names = { "--data-source-exclude" }, description = "file exclude pattern for data sources")
-    String dataSourceExcludePattern;
-
     @Option(names = { "--output-encoding" }, description = "encoding of output, e.g. UTF-8", defaultValue = "UTF-8")
     String outputEncoding;
 
@@ -117,7 +102,7 @@ public class Main implements Callable<Integer> {
     final String[] args;
 
     /** User-supplied writer (used mainly for unit testing) */
-    Writer userSuppliedWriter;
+    Writer callerSuppliedWriter;
 
     /** Injected by Picocli */
     @Spec private CommandSpec spec;
@@ -126,13 +111,13 @@ public class Main implements Callable<Integer> {
         this.args = new String[0];
     }
 
-    private Main(String[] args) {
+    Main(String[] args) {
         this.args = requireNonNull(args);
     }
 
-    private Main(String[] args, Writer userSuppliedWriter) {
+    private Main(String[] args, Writer callerSuppliedWriter) {
         this.args = requireNonNull(args);
-        this.userSuppliedWriter = requireNonNull(userSuppliedWriter);
+        this.callerSuppliedWriter = requireNonNull(callerSuppliedWriter);
     }
 
     public static void main(String[] args) {
@@ -171,77 +156,46 @@ public class Main implements Callable<Integer> {
         final String currentConfigFile = isNotEmpty(configFile) ? configFile : getDefaultConfigFileName();
         final Properties configuration = loadFreeMarkerCliConfiguration(currentConfigFile);
         final List<File> templateDirectories = getTemplateDirectories(templateDir);
-        final Settings settings = settings(configuration, templateDirectories);
+        final Settings settings = settings(configuration, templateDirectories, outputGeneratorDefinitions);
 
         try {
-            final FreeMarkerTask freeMarkerTask = new FreeMarkerTask(settings);
+            final FreeMarkerTask freeMarkerTask = new FreeMarkerTask(
+                    configurationSupplier(settings),
+                    outputGeneratorsSupplier(settings),
+                    () -> MapBuilder.merge(asList(toolsSupplier(settings).get(), settings.getUserParameters()))
+            );
             return freeMarkerTask.call();
         } finally {
-            if (settings.hasOutputs()) {
-                ClosableUtils.closeQuietly(settings.getWriter());
-            }
+            ClosableUtils.closeQuietly(settings.getCallerSuppliedWriter());
         }
     }
 
     void validate() {
-        // "-d" or "--data-source" parameter shall not contain wildcard characters
-        if (dataSources != null) {
-            for (String source : dataSources) {
-                if (isFileSource(source) && (source.contains("*") || source.contains("?"))) {
-                    throw new ParameterException(spec.commandLine(), "No wildcards supported for data source: " + source);
-                }
-            }
-        }
-
-        // does the templates match the expected outputs?!
-        // -) no output means it goes to stdout
-        // -) for each template there should be an output
-        final List<String> templates = templateSourceOptions.templates;
-        if (templates != null && templates.size() > 1) {
-            if (outputs != null && outputs.size() != templates.size()) {
-                throw new ParameterException(spec.commandLine(), "Template output does not match specified templates");
-            }
-        }
+        outputGeneratorDefinitions.forEach(t -> t.validate(spec.commandLine()));
     }
 
-    private Settings settings(Properties configuration, List<File> templateDirectories) {
+    private Settings settings(Properties configuration, List<File> templateDirectories, List<OutputGeneratorDefinition> outputGeneratorDefinitions) {
         final ParameterModelSupplier parameterModelSupplier = new ParameterModelSupplier(parameters);
 
         return Settings.builder()
                 .isReadFromStdin(readFromStdin)
-                .setArgs(args)
+                .setCommandLineArgs(args)
                 .setConfiguration(configuration)
-                .setDataModels(dataModels)
-                .setDataSources(getCombinedDataSources())
-                .setDataSourceIncludePattern(dataSourceIncludePattern)
-                .setDataSourceExcludePattern(dataSourceExcludePattern)
+                .setTemplateDirectories(templateDirectories)
+                .setOutputGeneratorDefinitions(outputGeneratorDefinitions)
+                .setSources(getDataSources())
+                .setSourceIncludePattern(dataSourceIncludePattern)
+                .setSourceExcludePattern(dataSourceExcludePattern)
                 .setInputEncoding(inputEncoding)
-                .setInteractiveTemplate(templateSourceOptions.interactiveTemplate)
                 .setLocale(locale)
                 .setOutputEncoding(outputEncoding)
-                .setOutputs(outputs)
                 .setParameters(parameterModelSupplier.get())
                 .setSystemProperties(systemProperties != null ? systemProperties : new Properties())
                 .setTemplateDirectories(templateDirectories)
-                .setTemplateNames(templateSourceOptions.templates)
-                .setWriter(writer(outputs, outputEncoding))
+                .setCallerSuppliedWriter(callerSuppliedWriter)
                 .build();
     }
 
-    private Writer writer(List<String> outputFiles, String outputEncoding) {
-        try {
-            if (userSuppliedWriter != null) {
-                return userSuppliedWriter;
-            } else if (ListUtils.isNullOrEmpty(outputFiles)) {
-                return new BufferedWriter(new OutputStreamWriter(System.out, outputEncoding));
-            } else {
-                return null;
-            }
-        } catch (IOException e) {
-            throw new RuntimeException("Unable to create writer", e);
-        }
-    }
-
     private void updateSystemProperties() {
         if (systemProperties != null && !systemProperties.isEmpty()) {
             System.getProperties().putAll(systemProperties);
@@ -254,11 +208,12 @@ public class Main implements Callable<Integer> {
      *
      * @return List of data sources
      */
-    private List<String> getCombinedDataSources() {
-        return Stream.of(dataSources, sources)
-                .filter(Objects::nonNull)
-                .flatMap(Collection::stream)
-                .collect(Collectors.toList());
+    private List<String> getDataSources() {
+        if (sources != null) {
+            return new ArrayList<>(sources);
+        } else {
+            return Collections.emptyList();
+        }
     }
 
     private static List<File> getTemplateDirectories(String additionalTemplateDir) {
@@ -288,14 +243,4 @@ public class Main implements Callable<Integer> {
             return new Properties();
         }
     }
-
-    private static boolean isFileSource(String source) {
-        if (source.contains("file://")) {
-            return true;
-        } else if (source.contains("://")) {
-            return false;
-        } else {
-            return true;
-        }
-    }
 }
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/OutputGeneratorsSupplier.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/OutputGeneratorsSupplier.java
new file mode 100644
index 0000000..b49dce0
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/OutputGeneratorsSupplier.java
@@ -0,0 +1,107 @@
+package org.apache.freemarker.generator.cli.config;
+
+import org.apache.freemarker.generator.base.datasource.DataSource;
+import org.apache.freemarker.generator.base.datasource.DataSourcesSupplier;
+import org.apache.freemarker.generator.base.output.OutputGenerator;
+import org.apache.freemarker.generator.base.template.TemplateTransformation;
+import org.apache.freemarker.generator.base.template.TemplateTransformationsBuilder;
+import org.apache.freemarker.generator.cli.picocli.OutputGeneratorDefinition;
+import org.apache.freemarker.generator.cli.picocli.TemplateOutputDefinition;
+import org.apache.freemarker.generator.cli.picocli.TemplateSourceDefinition;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+public class OutputGeneratorsSupplier implements Supplier<List<OutputGenerator>> {
+
+    private final Settings settings;
+
+    public OutputGeneratorsSupplier(Settings settings) {
+        this.settings = settings;
+    }
+
+    @Override
+    public List<OutputGenerator> get() {
+        return settings.getOutputGeneratorDefinitions().stream()
+                .map(this::outputGenerator)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toList());
+    }
+
+    private List<OutputGenerator> outputGenerator(OutputGeneratorDefinition definition) {
+        final List<OutputGenerator> result = new ArrayList<>();
+        final TemplateSourceDefinition templateSourceDefinition = definition.templateSourceDefinition;
+        final TemplateOutputDefinition templateOutputDefinition = definition.templateOutputDefinition;
+        final TemplateTransformationsBuilder builder = TemplateTransformationsBuilder.builder();
+
+        // set the template
+        if (templateSourceDefinition.isInteractiveTemplate()) {
+            builder.setInteractiveTemplate(templateSourceDefinition.interactiveTemplate);
+        } else {
+            builder.addTemplateSource(templateSourceDefinition.template);
+        }
+
+        // set the writer
+        builder.setUserSuppliedWriter(settings.getCallerSuppliedWriter());
+
+        // set template output
+        if (templateOutputDefinition != null) {
+            builder.addOutputs(templateOutputDefinition.outputs);
+        }
+
+        // set template filter
+        if (definition.hasTemplateSourceIncludes()) {
+            builder.addInclude(definition.getTemplateSourceFilterDefinition().templateIncludePatterns.get(0));
+        }
+
+        if (definition.hasTemplateSourceExcludes()) {
+            builder.addExclude(definition.getTemplateSourceFilterDefinition().templateExcludePatterns.get(0));
+        }
+
+        final List<TemplateTransformation> templateTransformations = builder.build();
+
+        for (TemplateTransformation templateTransformation : templateTransformations) {
+            final OutputGenerator outputGenerator = new OutputGenerator(
+                    templateTransformation.getTemplateSource(),
+                    templateTransformation.getTemplateOutput(),
+                    dataSources(definition),
+                    dataModels(definition)
+            );
+            result.add(outputGenerator);
+        }
+
+        return result;
+    }
+
+    private List<DataSource> dataSources(OutputGeneratorDefinition outputGeneratorDefinition) {
+        final ArrayList<DataSource> result = new ArrayList<>();
+
+        final DataSourcesSupplier sharedDataSourcesSupplier = new DataSourcesSupplier(
+                settings.getSources(),
+                settings.getSourceIncludePattern(),
+                settings.getSourceExcludePattern(),
+                settings.getInputEncoding()
+        );
+
+        result.addAll(sharedDataSourcesSupplier.get());
+
+        final DataSourcesSupplier outputGeneratorDataSourcesSupplier = new DataSourcesSupplier(
+                outputGeneratorDefinition.getDataSources(),
+                settings.getSourceIncludePattern(),
+                settings.getSourceExcludePattern(),
+                settings.getInputEncoding()
+        );
+
+        result.addAll(outputGeneratorDataSourcesSupplier.get());
+
+        return result;
+    }
+
+    private Map<String, Object> dataModels(OutputGeneratorDefinition outputGeneratorDefinition) {
+        return new DataModelSupplier(outputGeneratorDefinition.getDataModels()).get();
+    }
+}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Settings.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Settings.java
index 427fd93..8959a3f 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Settings.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Settings.java
@@ -17,9 +17,9 @@
 package org.apache.freemarker.generator.cli.config;
 
 import org.apache.freemarker.generator.base.FreeMarkerConstants.Model;
-import org.apache.freemarker.generator.base.util.ListUtils;
 import org.apache.freemarker.generator.base.util.LocaleUtils;
 import org.apache.freemarker.generator.base.util.NonClosableWriterWrapper;
+import org.apache.freemarker.generator.cli.picocli.OutputGeneratorDefinition;
 
 import java.io.File;
 import java.io.Writer;
@@ -38,7 +38,6 @@ import static java.util.Objects.requireNonNull;
 import static org.apache.freemarker.generator.base.FreeMarkerConstants.Configuration.LOCALE_KEY;
 import static org.apache.freemarker.generator.base.FreeMarkerConstants.DEFAULT_CHARSET;
 import static org.apache.freemarker.generator.base.FreeMarkerConstants.DEFAULT_LOCALE;
-import static org.apache.freemarker.generator.base.util.StringUtils.isEmpty;
 
 /**
  * Capture all the settings required for rendering a FreeMarker template.
@@ -49,22 +48,22 @@ public class Settings {
     private final Properties configuration;
 
     /** Command line arguments */
-    private final List<String> args;
+    private final List<String> commandLineArgs;
 
     /** List of FreeMarker template directories to be passed to FreeMarker <code>TemplateLoader</code> */
     private final List<File> templateDirectories;
 
-    /** List of template to be loaded and rendered */
-    private final List<String> templates;
+    /** User-provided output generators (transformations) */
+    private final List<OutputGeneratorDefinition> outputGeneratorDefinitions;
 
-    /** Template provided by the user interactively */
-    private final String interactiveTemplate;
+    /** List of additional sources provided by positional parameters */
+    private final List<String> sources;
 
-    /** Optional include pattern for recursive directly search of template files */
-    private final String templateFileIncludePattern;
+    /** Include pattern for sources */
+    private final String sourceIncludePattern;
 
-    /** Optional exclude pattern for recursive directly search of data source files */
-    private final String templateFileExcludePattern;
+    /** Exclude pattern for sources */
+    private final String sourceExcludePattern;
 
     /** Encoding of input files */
     private final Charset inputEncoding;
@@ -75,81 +74,53 @@ public class Settings {
     /** Enable verbose mode (currently not used) **/
     private final boolean verbose;
 
-    /** Optional output files or directories if not written to stdout */
-    private final List<String> outputs;
-
-    /** Optional include pattern for recursive directly search of data source files */
-    private final String dataSourceIncludePattern;
-
-    /** Optional exclude pattern for recursive directly search of data source files */
-    private final String dataSourceExcludePattern;
-
     /** The locale used for rendering the template */
     private final Locale locale;
 
     /** Read from stdin? */
     private final boolean isReadFromStdin;
 
-    /** User-supplied list of data sources or directories */
-    private final List<String> dataSources;
-
-    /** User-supplied list of data sources directly exposed in the data model */
-    private final List<String> dataModels;
-
     /** User-supplied parameters */
     private final Map<String, Object> userParameters;
 
     /** User-supplied system properties */
     private final Properties userSystemProperties;
 
-    /** The writer used for rendering templates, e.g. stdout or a file writer */
-    private final Writer writer;
+    /** Caller-supplied writer */
+    private final Writer callerSuppliedWriter;
 
     private Settings(
             Properties configuration,
-            List<String> args,
+            List<String> commandLineArgs,
             List<File> templateDirectories,
-            List<String> templates,
-            String interactiveTemplate,
-            String templateFileIncludePattern,
-            String templateFileExcludePattern,
+            List<OutputGeneratorDefinition> outputGeneratorDefinitions,
+            List<String> sources,
+            String sourceIncludePattern,
+            String sourceExcludePattern,
             Charset inputEncoding,
             Charset outputEncoding,
             boolean verbose,
-            List<String> outputs,
-            String dataSourceIncludePattern,
-            String dataSourceExcludePattern,
             Locale locale,
             boolean isReadFromStdin,
-            List<String> dataSources,
-            List<String> dataModels,
             Map<String, Object> userParameters,
             Properties userSystemProperties,
-            Writer writer) {
-        if ((templates == null || templates.isEmpty()) && isEmpty(interactiveTemplate)) {
-            throw new IllegalArgumentException("Either 'template' or 'interactiveTemplate' must be provided");
-        }
+            Writer callerSuppliedWriter) {
 
-        this.args = requireNonNull(args);
+        this.commandLineArgs = requireNonNull(commandLineArgs);
         this.templateDirectories = requireNonNull(templateDirectories);
-        this.templates = requireNonNull(templates);
-        this.interactiveTemplate = interactiveTemplate;
-        this.templateFileIncludePattern = templateFileIncludePattern;
-        this.templateFileExcludePattern = templateFileExcludePattern;
+        this.outputGeneratorDefinitions = requireNonNull(outputGeneratorDefinitions);
+        this.sources = requireNonNull(sources);
+        this.sourceIncludePattern = sourceIncludePattern;
+        this.sourceExcludePattern = sourceExcludePattern;
         this.inputEncoding = inputEncoding;
         this.outputEncoding = outputEncoding;
         this.verbose = verbose;
-        this.outputs = outputs;
-        this.dataSourceIncludePattern = dataSourceIncludePattern;
-        this.dataSourceExcludePattern = dataSourceExcludePattern;
         this.locale = requireNonNull(locale);
         this.isReadFromStdin = isReadFromStdin;
-        this.dataSources = requireNonNull(dataSources);
-        this.dataModels = requireNonNull(dataModels);
         this.userParameters = requireNonNull(userParameters);
         this.userSystemProperties = requireNonNull(userSystemProperties);
         this.configuration = requireNonNull(configuration);
-        this.writer = writer != null ? new NonClosableWriterWrapper(writer) : null;
+        this.callerSuppliedWriter = callerSuppliedWriter != null ? new NonClosableWriterWrapper(callerSuppliedWriter) : null;
     }
 
     public static SettingsBuilder builder() {
@@ -160,28 +131,28 @@ public class Settings {
         return configuration;
     }
 
-    public List<String> getArgs() {
-        return args;
+    public List<String> getCommandLineArgs() {
+        return commandLineArgs;
     }
 
     public List<File> getTemplateDirectories() {
         return templateDirectories;
     }
 
-    public List<String> getTemplates() {
-        return templates;
+    public List<OutputGeneratorDefinition> getOutputGeneratorDefinitions() {
+        return outputGeneratorDefinitions;
     }
 
-    public String getInteractiveTemplate() {
-        return interactiveTemplate;
+    public List<String> getSources() {
+        return sources;
     }
 
-    public String getTemplateFileIncludePattern() {
-        return templateFileIncludePattern;
+    public String getSourceIncludePattern() {
+        return sourceIncludePattern;
     }
 
-    public String getTemplateFileExcludePattern() {
-        return templateFileExcludePattern;
+    public String getSourceExcludePattern() {
+        return sourceExcludePattern;
     }
 
     public Charset getInputEncoding() {
@@ -200,18 +171,6 @@ public class Settings {
         return verbose;
     }
 
-    public List<String> getOutputs() {
-        return outputs;
-    }
-
-    public String getDataSourceIncludePattern() {
-        return dataSourceIncludePattern;
-    }
-
-    public String getDataSourceExcludePattern() {
-        return dataSourceExcludePattern;
-    }
-
     public Locale getLocale() {
         return locale;
     }
@@ -220,14 +179,6 @@ public class Settings {
         return isReadFromStdin;
     }
 
-    public List<String> getDataSources() {
-        return dataSources;
-    }
-
-    public List<String> getDataModels() {
-        return dataModels;
-    }
-
     public Map<String, Object> getUserParameters() {
         return userParameters;
     }
@@ -236,12 +187,8 @@ public class Settings {
         return userSystemProperties;
     }
 
-    public boolean hasOutputs() {
-        return ListUtils.isNotEmpty(outputs);
-    }
-
-    public Writer getWriter() {
-        return writer;
+    public Writer getCallerSuppliedWriter() {
+        return callerSuppliedWriter;
     }
 
     /**
@@ -252,84 +199,73 @@ public class Settings {
      */
     public Map<String, Object> toMap() {
         final Map<String, Object> result = new HashMap<>();
-        result.put(Model.FREEMARKER_CLI_ARGS, getArgs());
+        result.put(Model.FREEMARKER_CLI_ARGS, getCommandLineArgs());
         result.put(Model.FREEMARKER_LOCALE, getLocale());
         result.put(Model.FREEMARKER_TEMPLATE_DIRECTORIES, getTemplateDirectories());
         result.put(Model.FREEMARKER_USER_PARAMETERS, getUserParameters());
         result.put(Model.FREEMARKER_USER_SYSTEM_PROPERTIES, getUserSystemProperties());
-        result.put(Model.FREEMARKER_WRITER, getWriter());
+        result.put(Model.FREEMARKER_WRITER, getCallerSuppliedWriter());
         return result;
     }
 
-    public boolean isInteractiveTemplate() {
-        return interactiveTemplate != null;
-    }
-
     @Override
     public String toString() {
         return "Settings{" +
                 "configuration=" + configuration +
-                ", args=" + args +
+                ", commandLineArgs=" + commandLineArgs +
                 ", templateDirectories=" + templateDirectories +
-                ", templateName=s'" + templates + '\'' +
-                ", interactiveTemplate='" + interactiveTemplate + '\'' +
-                ", templateFileIncludePattern='" + templateFileIncludePattern + '\'' +
-                ", templateFileExcludePattern='" + templateFileExcludePattern + '\'' +
+                ", outputGeneratorDefinitions=" + outputGeneratorDefinitions +
                 ", inputEncoding=" + inputEncoding +
                 ", outputEncoding=" + outputEncoding +
                 ", verbose=" + verbose +
-                ", outputs=" + outputs +
-                ", include='" + dataSourceIncludePattern + '\'' +
-                ", exclude='" + dataSourceExcludePattern + '\'' +
                 ", locale=" + locale +
                 ", isReadFromStdin=" + isReadFromStdin +
-                ", dataSources=" + dataSources +
                 ", userParameters=" + userParameters +
                 ", userSystemProperties=" + userSystemProperties +
+                ", writer=" + callerSuppliedWriter +
+                ", templateEncoding=" + getTemplateEncoding() +
+                ", readFromStdin=" + isReadFromStdin() +
+                ", toMap=" + toMap() +
                 '}';
     }
 
     public static class SettingsBuilder {
-        private List<String> args;
+        private List<String> commandLineArgs;
         private List<File> templateDirectories;
-        private List<String> templateNames;
-        private String interactiveTemplate;
-        private String templateFileIncludePattern;
-        private String templateFileExcludePattern;
+        private List<OutputGeneratorDefinition> outputGeneratorDefinitions;
+        private List<String> sources;
+        private String sourceIncludePattern;
+        private String sourceExcludePattern;
         private String inputEncoding;
         private String outputEncoding;
         private boolean verbose;
-        private List<String> outputs;
-        private String dataSourceIncludePattern;
-        private String dataSourceExcludePattern;
         private String locale;
         private boolean isReadFromStdin;
-        private List<String> dataSources;
-        private List<String> dataModels;
         private Map<String, Object> parameters;
         private Properties systemProperties;
         private Properties configuration;
-        private Writer writer;
+        private Writer callerSuppliedWriter;
 
         private SettingsBuilder() {
-            this.args = emptyList();
+            this.commandLineArgs = emptyList();
+            this.templateDirectories = emptyList();
+            this.outputGeneratorDefinitions = emptyList();
+            this.sources = emptyList();
+            this.sourceIncludePattern = null;
+            this.sourceExcludePattern = null;
             this.configuration = new Properties();
             this.locale = DEFAULT_LOCALE.toString();
             this.parameters = new HashMap<>();
             this.systemProperties = new Properties();
             this.setInputEncoding(DEFAULT_CHARSET.name());
             this.setOutputEncoding(DEFAULT_CHARSET.name());
-            this.templateNames = new ArrayList<>();
-            this.dataSources = emptyList();
-            this.dataModels = emptyList();
-            this.templateDirectories = emptyList();
         }
 
-        public SettingsBuilder setArgs(String[] args) {
-            if (args == null) {
-                this.args = emptyList();
+        public SettingsBuilder setCommandLineArgs(String[] commandLineArgs) {
+            if (commandLineArgs == null) {
+                this.commandLineArgs = emptyList();
             } else {
-                this.args = Arrays.asList(args);
+                this.commandLineArgs = Arrays.asList(commandLineArgs);
             }
 
             return this;
@@ -340,25 +276,23 @@ public class Settings {
             return this;
         }
 
-        public SettingsBuilder setTemplateNames(List<String> templateNames) {
-            if (templateNames != null) {
-                this.templateNames = templateNames;
-            }
+        public SettingsBuilder setOutputGeneratorDefinitions(List<OutputGeneratorDefinition> outputGeneratorDefinitions) {
+            this.outputGeneratorDefinitions = new ArrayList<>(outputGeneratorDefinitions);
             return this;
         }
 
-        public SettingsBuilder setInteractiveTemplate(String interactiveTemplate) {
-            this.interactiveTemplate = interactiveTemplate;
+        public SettingsBuilder setSources(List<String> sources) {
+            this.sources = new ArrayList<>(sources);
             return this;
         }
 
-        public SettingsBuilder setTemplateFileIncludePattern(String templateFileIncludePattern) {
-            this.templateFileIncludePattern = templateFileIncludePattern;
+        public SettingsBuilder setSourceIncludePattern(String sourceIncludePattern) {
+            this.sourceIncludePattern = sourceIncludePattern;
             return this;
         }
 
-        public SettingsBuilder setTemplateFileExcludePattern(String templateFileExcludePattern) {
-            this.templateFileExcludePattern = templateFileExcludePattern;
+        public SettingsBuilder setSourceExcludePattern(String sourceExcludePattern) {
+            this.sourceExcludePattern = sourceExcludePattern;
             return this;
         }
 
@@ -381,23 +315,6 @@ public class Settings {
             return this;
         }
 
-        public SettingsBuilder setOutputs(List<String> outputs) {
-            if (outputs != null) {
-                this.outputs = outputs;
-            }
-            return this;
-        }
-
-        public SettingsBuilder setDataSourceIncludePattern(String dataSourceIncludePattern) {
-            this.dataSourceIncludePattern = dataSourceIncludePattern;
-            return this;
-        }
-
-        public SettingsBuilder setDataSourceExcludePattern(String dataSourceExcludePattern) {
-            this.dataSourceExcludePattern = dataSourceExcludePattern;
-            return this;
-        }
-
         public SettingsBuilder setLocale(String locale) {
             this.locale = locale;
             return this;
@@ -408,20 +325,6 @@ public class Settings {
             return this;
         }
 
-        public SettingsBuilder setDataSources(List<String> dataSources) {
-            if (dataSources != null) {
-                this.dataSources = dataSources;
-            }
-            return this;
-        }
-
-        public SettingsBuilder setDataModels(List<String> dataModels) {
-            if (dataModels != null) {
-                this.dataModels = dataModels;
-            }
-            return this;
-        }
-
         public SettingsBuilder setParameters(Map<String, Object> parameters) {
             if (parameters != null) {
                 this.parameters = parameters;
@@ -443,8 +346,8 @@ public class Settings {
             return this;
         }
 
-        public SettingsBuilder setWriter(Writer writer) {
-            this.writer = writer;
+        public SettingsBuilder setCallerSuppliedWriter(Writer callerSuppliedWriter) {
+            this.callerSuppliedWriter = callerSuppliedWriter;
             return this;
         }
 
@@ -455,25 +358,20 @@ public class Settings {
 
             return new Settings(
                     configuration,
-                    args,
+                    commandLineArgs,
                     templateDirectories,
-                    templateNames,
-                    interactiveTemplate,
-                    templateFileIncludePattern,
-                    templateFileExcludePattern,
+                    outputGeneratorDefinitions,
+                    sources,
+                    sourceIncludePattern,
+                    sourceExcludePattern,
                     inputEncoding,
                     outputEncoding,
                     verbose,
-                    outputs,
-                    dataSourceIncludePattern,
-                    dataSourceExcludePattern,
                     LocaleUtils.parseLocale(currLocale),
                     isReadFromStdin,
-                    dataSources,
-                    dataModels,
                     parameters,
                     systemProperties,
-                    writer
+                    callerSuppliedWriter
             );
         }
 
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Suppliers.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Suppliers.java
index a7bc21d..793318e 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Suppliers.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Suppliers.java
@@ -17,15 +17,10 @@
 package org.apache.freemarker.generator.cli.config;
 
 import freemarker.cache.TemplateLoader;
-import org.apache.freemarker.generator.base.datasource.DataSourcesSupplier;
 import org.apache.freemarker.generator.base.file.PropertiesClassPathSupplier;
 import org.apache.freemarker.generator.base.file.PropertiesFileSystemSupplier;
 import org.apache.freemarker.generator.base.file.PropertiesSupplier;
-import org.apache.freemarker.generator.base.template.TemplateTransformation;
-import org.apache.freemarker.generator.base.template.TemplateTransformationsBuilder;
 
-import java.util.List;
-import java.util.Map;
 import java.util.function.Supplier;
 
 /**
@@ -53,30 +48,8 @@ public class Suppliers {
         return new ToolsSupplier(settings.getConfiguration(), settings.toMap());
     }
 
-    public static DataSourcesSupplier dataSourcesSupplier(Settings settings) {
-        return new DataSourcesSupplier(settings.getDataSources(),
-                settings.getDataSourceIncludePattern(),
-                settings.getDataSourceExcludePattern(),
-                settings.getInputEncoding());
-    }
-
-    public static DataModelSupplier dataModelSupplier(Settings settings) {
-        return new DataModelSupplier(settings.getDataModels());
-    }
-
-    public static Supplier<Map<String, Object>> parameterSupplier(Settings settings) {
-        return settings::getUserParameters;
-    }
-
-    public static Supplier<List<TemplateTransformation>> templateTransformationsSupplier(Settings settings) {
-        return () -> TemplateTransformationsBuilder.builder()
-                .setInteractiveTemplate(settings.getInteractiveTemplate())
-                .addTemplateSources(settings.getTemplates())
-                .addInclude(settings.getTemplateFileIncludePattern())
-                .addExclude(settings.getTemplateFileExcludePattern())
-                .addOutputs(settings.getOutputs())
-                .setWriter(settings.getWriter())
-                .build();
+    public static OutputGeneratorsSupplier outputGeneratorsSupplier(Settings settings) {
+        return new OutputGeneratorsSupplier(settings);
     }
 
     public static PropertiesSupplier propertiesSupplier(String fileName) {
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataModelDefinition.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataModelDefinition.java
new file mode 100644
index 0000000..0c4ffcf
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataModelDefinition.java
@@ -0,0 +1,30 @@
+/*
+ * 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.generator.cli.picocli;
+
+import picocli.CommandLine.Option;
+
+import java.util.List;
+
+/**
+ * User-supplied list of data sources directly exposed in the data model
+ */
+public class DataModelDefinition {
+
+    @Option(names = { "-m", "--data-model" }, description = "data model used for rendering")
+    public List<String> dataModels;
+}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataSourceDefinition.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataSourceDefinition.java
new file mode 100644
index 0000000..3be4f08
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataSourceDefinition.java
@@ -0,0 +1,28 @@
+/*
+ * 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.generator.cli.picocli;
+
+import picocli.CommandLine.Option;
+
+import java.util.List;
+
+/** User-supplied list of data sources or directories */
+public class DataSourceDefinition {
+
+    @Option(names = { "-s", "--data-source" }, description = "data source used for rendering")
+    public List<String> dataSources;
+}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataSourceFilterDefinition.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataSourceFilterDefinition.java
new file mode 100644
index 0000000..d1c3c44
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataSourceFilterDefinition.java
@@ -0,0 +1,30 @@
+/*
+ * 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.generator.cli.picocli;
+
+import picocli.CommandLine.Option;
+
+import java.util.List;
+
+public class DataSourceFilterDefinition {
+
+    @Option(names = { "--data-source-include" }, description = "data source include pattern")
+    public List<String> dataSourceIncludePatterns;
+
+    @Option(names = { "--data-source-exclude" }, description = "data source exclude pattern")
+    public List<String> dataSourceExcludePatterns;
+}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/OutputGeneratorDefinition.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/OutputGeneratorDefinition.java
new file mode 100644
index 0000000..d35c277
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/OutputGeneratorDefinition.java
@@ -0,0 +1,117 @@
+/*
+ * 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.generator.cli.picocli;
+
+import picocli.CommandLine;
+import picocli.CommandLine.ArgGroup;
+import picocli.CommandLine.ParameterException;
+
+import java.util.List;
+
+import static java.util.Collections.emptyList;
+
+public class OutputGeneratorDefinition {
+
+    @ArgGroup(multiplicity = "1")
+    public TemplateSourceDefinition templateSourceDefinition;
+
+    @ArgGroup(exclusive = false)
+    public TemplateSourceFilterDefinition templateSourceFilterDefinition;
+
+    @ArgGroup(exclusive = false)
+    public TemplateOutputDefinition templateOutputDefinition;
+
+    @ArgGroup(exclusive = false)
+    public DataSourceDefinition dataSourceDefinition;
+
+    @ArgGroup(exclusive = false)
+    public DataModelDefinition dataModelDefinition;
+
+    public void validate(CommandLine commandLine) {
+        if (templateOutputDefinition != null && templateOutputDefinition.outputs.size() > 1) {
+            throw new ParameterException(commandLine, "More than one output defined for a template");
+        }
+
+        if (dataSourceDefinition != null && dataSourceDefinition.dataSources != null) {
+            for (String source : dataSourceDefinition.dataSources) {
+                if (isFileSource(source) && (source.contains("*") || source.contains("?"))) {
+                    throw new ParameterException(commandLine, "No wildcards supported for data source: " + source);
+                }
+            }
+
+        }
+    }
+
+    public List<String> getDataSources() {
+        if (dataSourceDefinition != null && dataSourceDefinition.dataSources != null) {
+            return dataSourceDefinition.dataSources;
+        } else {
+            return emptyList();
+        }
+    }
+
+    public List<String> getDataModels() {
+        if (dataModelDefinition != null && dataModelDefinition.dataModels != null) {
+            return dataModelDefinition.dataModels;
+        } else {
+            return emptyList();
+        }
+
+    }
+
+    public TemplateSourceDefinition getTemplateSourceDefinition() {
+        return templateSourceDefinition;
+    }
+
+    public TemplateSourceFilterDefinition getTemplateSourceFilterDefinition() {
+        return templateSourceFilterDefinition;
+    }
+
+    public TemplateOutputDefinition getTemplateOutputDefinition() {
+        return templateOutputDefinition;
+    }
+
+    public DataSourceDefinition getDataSourceDefinition() {
+        return dataSourceDefinition;
+    }
+
+    public DataModelDefinition getDataModelDefinition() {
+        return dataModelDefinition;
+    }
+
+    public boolean hasTemplateSourceIncludes() {
+        return getTemplateSourceFilterDefinition() != null &&
+                getTemplateSourceFilterDefinition().templateIncludePatterns != null &&
+                !getTemplateSourceFilterDefinition().templateIncludePatterns.isEmpty();
+    }
+
+    public boolean hasTemplateSourceExcludes() {
+        return getTemplateSourceFilterDefinition() != null &&
+                getTemplateSourceFilterDefinition().templateExcludePatterns != null &&
+                !getTemplateSourceFilterDefinition().templateExcludePatterns.isEmpty();
+    }
+
+    private static boolean isFileSource(String source) {
+        if (source.contains("file://")) {
+            return true;
+        } else if (source.contains("://")) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateOutputDefinition.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateOutputDefinition.java
new file mode 100644
index 0000000..59627e3
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateOutputDefinition.java
@@ -0,0 +1,31 @@
+/*
+ * 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.generator.cli.picocli;
+
+import picocli.CommandLine.Option;
+
+import java.util.List;
+
+public class TemplateOutputDefinition {
+
+    @Option(names = { "-o", "--output" }, description = "output files or directories")
+    public List<String> outputs;
+
+    public boolean hasOutput() {
+        return outputs != null && !outputs.isEmpty();
+    }
+}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateSourceDefinition.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateSourceDefinition.java
new file mode 100644
index 0000000..6193c7b
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateSourceDefinition.java
@@ -0,0 +1,32 @@
+/*
+ * 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.generator.cli.picocli;
+
+import picocli.CommandLine.Option;
+
+public class TemplateSourceDefinition {
+
+    @Option(names = { "-t", "--template" }, description = "templates to process")
+    public String template;
+
+    @Option(names = { "-i", "--interactive" }, description = "interactive template to process")
+    public String interactiveTemplate;
+
+    public boolean isInteractiveTemplate() {
+        return interactiveTemplate != null && !interactiveTemplate.isEmpty();
+    }
+}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateSourceFilterDefinition.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateSourceFilterDefinition.java
new file mode 100644
index 0000000..68cce4e
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateSourceFilterDefinition.java
@@ -0,0 +1,31 @@
+/*
+ * 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.generator.cli.picocli;
+
+import picocli.CommandLine.Option;
+
+import java.util.List;
+
+/** Include/exclude pattern when processing template directories */
+public class TemplateSourceFilterDefinition {
+
+    @Option(names = { "--template-include" }, description = "template include pattern")
+    public List<String> templateIncludePatterns;
+
+    @Option(names = { "--template-exclude" }, description = "template exclude pattern")
+    public List<String> templateExcludePatterns;
+}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
index cc20d76..19a4634 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
@@ -20,104 +20,70 @@ import freemarker.template.Configuration;
 import freemarker.template.Template;
 import freemarker.template.TemplateException;
 import org.apache.commons.io.FileUtils;
-import org.apache.freemarker.generator.base.FreeMarkerConstants.Location;
-import org.apache.freemarker.generator.base.datasource.DataSource;
-import org.apache.freemarker.generator.base.datasource.DataSourceFactory;
 import org.apache.freemarker.generator.base.datasource.DataSources;
+import org.apache.freemarker.generator.base.output.OutputGenerator;
 import org.apache.freemarker.generator.base.template.TemplateOutput;
 import org.apache.freemarker.generator.base.template.TemplateSource;
-import org.apache.freemarker.generator.base.template.TemplateTransformation;
-import org.apache.freemarker.generator.base.util.UriUtils;
-import org.apache.freemarker.generator.cli.config.Settings;
 
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.Writer;
-import java.net.URI;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.function.Supplier;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.requireNonNull;
-import static org.apache.freemarker.generator.base.FreeMarkerConstants.DEFAULT_GROUP;
-import static org.apache.freemarker.generator.base.FreeMarkerConstants.Location.STDIN;
 import static org.apache.freemarker.generator.base.FreeMarkerConstants.Model.DATASOURCES;
-import static org.apache.freemarker.generator.base.mime.Mimetypes.MIME_TEXT_PLAIN;
-import static org.apache.freemarker.generator.cli.config.Suppliers.configurationSupplier;
-import static org.apache.freemarker.generator.cli.config.Suppliers.dataModelSupplier;
-import static org.apache.freemarker.generator.cli.config.Suppliers.dataSourcesSupplier;
-import static org.apache.freemarker.generator.cli.config.Suppliers.parameterSupplier;
-import static org.apache.freemarker.generator.cli.config.Suppliers.templateTransformationsSupplier;
-import static org.apache.freemarker.generator.cli.config.Suppliers.toolsSupplier;
 
 /**
  * Renders a FreeMarker template.
+ * <p>
+ * Implementation notes
+ * <ul>
+ *     <li>configurationSupplier provides the tools</li>
+ *     <li>outputGeneratorsSupplier handles STDIN already</li>
+ * </ul>
  */
 public class FreeMarkerTask implements Callable<Integer> {
 
     private static final int SUCCESS = 0;
 
-    private final Settings settings;
-    private final Supplier<Map<String, Object>> toolsSupplier;
-    private final Supplier<List<DataSource>> dataSourcesSupplier;
-    private final Supplier<Map<String, Object>> dataModelsSupplier;
-    private final Supplier<Map<String, Object>> parameterModelSupplier;
     private final Supplier<Configuration> configurationSupplier;
-    private final Supplier<List<TemplateTransformation>> templateTransformationsSupplier;
-
-
-    public FreeMarkerTask(Settings settings) {
-        this(settings,
-                configurationSupplier(settings),
-                templateTransformationsSupplier(settings),
-                dataSourcesSupplier(settings),
-                dataModelSupplier(settings),
-                parameterSupplier(settings),
-                toolsSupplier(settings)
-        );
-    }
-
-    public FreeMarkerTask(Settings settings,
-                          Supplier<Configuration> configurationSupplier,
-                          Supplier<List<TemplateTransformation>> templateTransformationsSupplier,
-                          Supplier<List<DataSource>> dataSourcesSupplier,
-                          Supplier<Map<String, Object>> dataModelsSupplier,
-                          Supplier<Map<String, Object>> parameterModelSupplier,
-                          Supplier<Map<String, Object>> toolsSupplier) {
-        this.settings = requireNonNull(settings);
-        this.toolsSupplier = requireNonNull(toolsSupplier);
-        this.dataSourcesSupplier = requireNonNull(dataSourcesSupplier);
-        this.dataModelsSupplier = requireNonNull(dataModelsSupplier);
-        this.parameterModelSupplier = requireNonNull(parameterModelSupplier);
-        this.configurationSupplier = requireNonNull(configurationSupplier);
-        this.templateTransformationsSupplier = requireNonNull(templateTransformationsSupplier);
+    private final Supplier<List<OutputGenerator>> outputGeneratorsSupplier;
+    private final Supplier<Map<String, Object>> sharedDataModelSupplier;
+
+    public FreeMarkerTask(Supplier<Configuration> configurationSupplier,
+                          Supplier<List<OutputGenerator>> outputGeneratorsSupplier,
+                          Supplier<Map<String, Object>> sharedDataModelSupplier) {
+        this.configurationSupplier = requireNonNull(configurationSupplier, "configurationSupplier");
+        this.outputGeneratorsSupplier = requireNonNull(outputGeneratorsSupplier, "outputGeneratorsSupplier");
+        this.sharedDataModelSupplier = requireNonNull(sharedDataModelSupplier, "sharedDataModelSupplier");
     }
 
     @Override
     public Integer call() {
-        try {
-            final Configuration configuration = configurationSupplier.get();
-            final List<TemplateTransformation> templateTransformations = templateTransformationsSupplier.get();
-            final DataSources dataSources = dataSources(settings, dataSourcesSupplier);
-            final Map<String, Object> dataModel = dataModel(dataSources, parameterModelSupplier, dataModelsSupplier, toolsSupplier);
-            templateTransformations.forEach(t -> process(configuration, t, dataModel));
-            return SUCCESS;
-        } catch (RuntimeException e) {
-            throw new RuntimeException("Failed to process templates", e);
-        }
+        final Configuration configuration = configurationSupplier.get();
+        final List<OutputGenerator> outputGenerators = outputGeneratorsSupplier.get();
+        final Map<String, Object> sharedDataModel = sharedDataModelSupplier.get();
+        outputGenerators.forEach(outputGenerator -> process(configuration, outputGenerator, sharedDataModel));
+        return SUCCESS;
     }
 
     private void process(Configuration configuration,
-                         TemplateTransformation templateTransformation,
-                         Map<String, Object> dataModel) {
-        final TemplateSource templateSource = templateTransformation.getTemplateSource();
-        final TemplateOutput templateOutput = templateTransformation.getTemplateOutput();
+                         OutputGenerator outputGenerator,
+                         Map<String, Object> sharedDataModelMap) {
+
+        final TemplateSource templateSource = outputGenerator.getTemplateSource();
+        final TemplateOutput templateOutput = outputGenerator.getTemplateOutput();
+        final DataSources dataSources = new DataSources(outputGenerator.getDataSources());
+        final Map<String, Object> dataModelMap = outputGenerator.getVariables();
+        final Map<String, Object> dataModel = toDataModel(dataSources, dataModelMap, sharedDataModelMap);
+
         try (Writer writer = writer(templateOutput)) {
             final Template template = template(configuration, templateSource);
             template.process(dataModel, writer);
@@ -126,34 +92,14 @@ public class FreeMarkerTask implements Callable<Integer> {
         }
     }
 
-    private static DataSources dataSources(Settings settings, Supplier<List<DataSource>> dataSourcesSupplier) {
-        final List<DataSource> dataSources = new ArrayList<>(dataSourcesSupplier.get());
-
-        // Add optional data source from STDIN at the start of the list since
-        // this allows easy sequence slicing in FreeMarker.
-        if (settings.isReadFromStdin()) {
-            final URI uri = UriUtils.toUri(Location.SYSTEM, "in");
-            dataSources.add(0, DataSourceFactory.fromInputStream(STDIN, DEFAULT_GROUP, uri, System.in, MIME_TEXT_PLAIN, UTF_8));
-        }
-
-        return new DataSources(dataSources);
-    }
-
-    private static Map<String, Object> dataModel(
-            DataSources dataSources,
-            Supplier<Map<String, Object>> parameterModelSupplier,
-            Supplier<Map<String, Object>> dataModelsSupplier,
-            Supplier<Map<String, Object>> tools) {
+    @SafeVarargs
+    private static Map<String, Object> toDataModel(DataSources dataSources, Map<String, Object>... maps) {
         final Map<String, Object> result = new HashMap<>();
-        result.putAll(dataModelsSupplier.get());
+        Arrays.stream(maps).forEach(result::putAll);
         result.put(DATASOURCES, dataSources);
-        result.putAll(parameterModelSupplier.get());
-        result.putAll(tools.get());
         return result;
     }
 
-    // ==============================================================
-
     private static Writer writer(TemplateOutput templateOutput) throws IOException {
         if (templateOutput.getWriter() != null) {
             return templateOutput.getWriter();
@@ -177,7 +123,7 @@ public class FreeMarkerTask implements Callable<Integer> {
     private static Template template(Configuration configuration, TemplateSource templateSource) {
         switch (templateSource.getOrigin()) {
             case TEMPLATE_LOADER:
-                return fromTemplateLoader(configuration, templateSource);
+                return fromTemplatePath(configuration, templateSource);
             case TEMPLATE_CODE:
                 return fromTemplateCode(configuration, templateSource);
             default:
@@ -185,7 +131,7 @@ public class FreeMarkerTask implements Callable<Integer> {
         }
     }
 
-    private static Template fromTemplateLoader(Configuration configuration, TemplateSource templateSource) {
+    private static Template fromTemplatePath(Configuration configuration, TemplateSource templateSource) {
         final String path = templateSource.getPath();
         try {
             return configuration.getTemplate(path);
diff --git a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/PicocliTest.java b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/PicocliTest.java
index 34077d5..4cf5be3 100644
--- a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/PicocliTest.java
+++ b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/PicocliTest.java
@@ -16,12 +16,15 @@
  */
 package org.apache.freemarker.generator.cli;
 
+import org.apache.freemarker.generator.cli.picocli.OutputGeneratorDefinition;
 import org.junit.Test;
 import picocli.CommandLine;
-import picocli.CommandLine.ParameterException;
+
+import java.util.List;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 public class PicocliTest {
 
@@ -35,8 +38,10 @@ public class PicocliTest {
 
     @Test
     public void shouldParseSinglePositionalParameter() {
-        assertEquals(ANY_FILE_URI, parse("-t", ANY_TEMPLATE, ANY_FILE_URI).sources.get(0));
-        assertNull(ANY_FILE, parse("-t", ANY_TEMPLATE, ANY_FILE_URI).dataSources);
+        final Main main = parse("-t", ANY_TEMPLATE, ANY_FILE_URI);
+
+        assertEquals(1, main.outputGeneratorDefinitions.size());
+        assertEquals(ANY_FILE_URI, main.sources.get(0));
     }
 
     @Test
@@ -53,33 +58,43 @@ public class PicocliTest {
 
     @Test
     public void shouldParseSingleNamedDataSource() {
-        assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, ANY_FILE).sources.get(0));
-        assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, "-s", ANY_FILE).dataSources.get(0));
-        assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, "--data-source", ANY_FILE).dataSources.get(0));
-        assertEquals(ANY_FILE_URI, parse("-t", ANY_TEMPLATE, "--data-source", ANY_FILE_URI).dataSources.get(0));
+        assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, "-s", ANY_FILE).outputGeneratorDefinitions.get(0)
+                .getDataSources()
+                .get(0));
+        assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, "--data-source", ANY_FILE).outputGeneratorDefinitions.get(0)
+                .getDataSources()
+                .get(0));
+        assertEquals(ANY_FILE_URI, parse("-t", ANY_TEMPLATE, "--data-source", ANY_FILE_URI).outputGeneratorDefinitions.get(0)
+                .getDataSources()
+                .get(0));
     }
 
     @Test
-    public void shouldParseMultipleNamedDataSource() {
+    public void shouldParseMultipleNamedDataSources() {
         final Main main = parse("-t", ANY_TEMPLATE, "-s", ANY_FILE, "--data-source", OTHER_FILE_URI);
 
-        assertEquals(ANY_FILE, main.dataSources.get(0));
-        assertEquals(OTHER_FILE_URI, main.dataSources.get(1));
+        assertEquals(ANY_FILE, main.outputGeneratorDefinitions.get(0).getDataSources().get(0));
+        assertEquals(OTHER_FILE_URI, main.outputGeneratorDefinitions.get(0).getDataSources().get(1));
         assertNull(main.sources);
     }
 
     @Test
     public void shouldParseSingleDataModel() {
-        assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, "-m", ANY_FILE).dataModels.get(0));
-        assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, "--data-model", ANY_FILE).dataModels.get(0));
+        assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, "-m", ANY_FILE).outputGeneratorDefinitions.get(0)
+                .getDataModels()
+                .get(0));
+        assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, "--data-model", ANY_FILE).outputGeneratorDefinitions.get(0)
+                .getDataModels()
+                .get(0));
     }
 
     @Test
     public void shouldParseMultipleDataModels() {
         final Main main = parse("-t", ANY_TEMPLATE, "-m", ANY_FILE, "--data-model", OTHER_FILE_URI);
+        final OutputGeneratorDefinition outputGeneratorDefinition = main.outputGeneratorDefinitions.get(0);
 
-        assertEquals(ANY_FILE, main.dataModels.get(0));
-        assertEquals(OTHER_FILE_URI, main.dataModels.get(1));
+        assertEquals(ANY_FILE, outputGeneratorDefinition.getDataModels().get(0));
+        assertEquals(OTHER_FILE_URI, outputGeneratorDefinition.getDataModels().get(1));
         assertNull(main.sources);
     }
 
@@ -102,25 +117,65 @@ public class PicocliTest {
     public void shouldParseSingleTemplate() {
         final Main main = parse("-t", ANY_TEMPLATE);
 
-        assertEquals(ANY_TEMPLATE, main.templateSourceOptions.templates.get(0));
+        assertEquals(ANY_TEMPLATE, main.outputGeneratorDefinitions.get(0).templateSourceDefinition.template);
     }
 
     @Test
     public void shouldParseInteractiveTemplate() {
         final Main main = parse("-i", INTERACTIVE_TEMPLATE);
 
-        assertEquals(INTERACTIVE_TEMPLATE, main.templateSourceOptions.interactiveTemplate);
+        assertEquals(INTERACTIVE_TEMPLATE, main.outputGeneratorDefinitions.get(0).templateSourceDefinition.interactiveTemplate);
     }
 
-    @Test(expected = ParameterException.class)
-    public void shouldThrowParameterExceptionForMismatchedTemplateOutput() {
-        final Main main = parse("-t", "foo.ftl", "-t", "bar.ftl", "-o", "foo.out");
+    @Test
+    public void shouldParseMultipleTemplates() {
+        final Main main = parse("-t", ANY_TEMPLATE, "--template", ANY_TEMPLATE);
+
+        assertEquals(2, main.outputGeneratorDefinitions.size());
+    }
+
+    @Test
+    public void shouldParseStdin() {
+        final Main main = parse("-t", ANY_TEMPLATE, "--stdin");
+
+        assertTrue(main.readFromStdin);
+    }
+
+    @Test
+    public void shouldParseComplexCommandLine01() {
+        final Main main = parse(
+                "--template", "template01.ftl", "--data-source", "datasource10.csv",
+                "-t", "template02.ftl", "-s", "datasource20.csv", "-s", "datasource21.csv",
+                "-i", "some-interactive-template01", "-s", "datasource30.csv", "-o", "out.txt",
+                "-i", "some-interactive-template02");
 
         main.validate();
+
+        final List<OutputGeneratorDefinition> defs = main.outputGeneratorDefinitions;
+        assertEquals(4, defs.size());
+
+        assertTrue(defs.get(0).templateSourceDefinition.template.equals("template01.ftl"));
+        assertTrue(defs.get(0).dataSourceDefinition.dataSources.size() == 1);
+        assertTrue(defs.get(0).dataSourceDefinition.dataSources.get(0).equals("datasource10.csv"));
+        assertTrue(defs.get(0).templateOutputDefinition == null);
+
+        assertTrue(defs.get(1).templateSourceDefinition.template.equals("template02.ftl"));
+        assertTrue(defs.get(1).dataSourceDefinition.dataSources.size() == 2);
+        assertTrue(defs.get(1).dataSourceDefinition.dataSources.get(0).equals("datasource20.csv"));
+        assertTrue(defs.get(1).dataSourceDefinition.dataSources.get(1).equals("datasource21.csv"));
+        assertTrue(defs.get(0).templateOutputDefinition == null);
+
+        assertTrue(defs.get(2).templateSourceDefinition.interactiveTemplate.equals("some-interactive-template01"));
+        assertTrue(defs.get(2).dataSourceDefinition.dataSources.size() == 1);
+        assertTrue(defs.get(2).dataSourceDefinition.dataSources.get(0).equals("datasource30.csv"));
+        assertTrue(defs.get(2).templateOutputDefinition.outputs.get(0).equals("out.txt"));
+
+        assertTrue(defs.get(3).templateSourceDefinition.interactiveTemplate.equals("some-interactive-template02"));
+
     }
 
     private static Main parse(String... args) {
-        final Main main = new Main();
+        final Main main = new Main(args);
         new CommandLine(main).parseArgs(args);
         return main;
     }
diff --git a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/CompositeGroupDemo.java b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/CompositeGroupDemo.java
index 45bad76..8b7f80a 100644
--- a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/CompositeGroupDemo.java
+++ b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/CompositeGroupDemo.java
@@ -14,7 +14,7 @@ public class CompositeGroupDemo {
     @ArgGroup(exclusive = false, multiplicity = "1..*")
     List<OutputGenerator> outputGenerators;
 
-    static class OutputDefinition {
+    static class TemplateOutputDefinition {
         @Option(names = { "-o", "--output" }, description = "output files or directories") List<String> outputs;
     }
 
@@ -22,20 +22,20 @@ public class CompositeGroupDemo {
         @Option(names = { "-s", "--data-source" }, description = "data source used for rendering") List<String> dataSources;
     }
 
-    static class TemplateDefinition {
+    static class TemplateSourceDefinition {
         @Option(names = { "-t", "--template" }, description = "templates to process") String template;
         @Option(names = { "-i", "--interactive" }, description = "interactive template to process") public String interactiveTemplate;
     }
 
     static class OutputGenerator {
         @ArgGroup(multiplicity = "1")
-        TemplateDefinition templateDefinition;
+        TemplateSourceDefinition templateSourceDefinition;
 
         @ArgGroup(exclusive = false)
         DataSourceDefinition dataSourceDefinition;
 
         @ArgGroup(exclusive = false)
-        OutputDefinition outputDefinition;
+        TemplateOutputDefinition templateOutputDefinition;
     }
 
     public static void main(String[] args) {
@@ -54,23 +54,23 @@ public class CompositeGroupDemo {
         Validate.notNull(outputGenerators);
         Validate.isTrue(outputGenerators.size() == 4);
 
-        Validate.isTrue(outputGenerators.get(0).templateDefinition.template.equals("template01.ftl"));
+        Validate.isTrue(outputGenerators.get(0).templateSourceDefinition.template.equals("template01.ftl"));
         Validate.isTrue(outputGenerators.get(0).dataSourceDefinition.dataSources.size() == 1);
         Validate.isTrue(outputGenerators.get(0).dataSourceDefinition.dataSources.get(0).equals("datasource10.csv"));
-        Validate.isTrue(outputGenerators.get(0).outputDefinition == null);
+        Validate.isTrue(outputGenerators.get(0).templateOutputDefinition == null);
 
-        Validate.isTrue(outputGenerators.get(1).templateDefinition.template.equals("template02.ftl"));
+        Validate.isTrue(outputGenerators.get(1).templateSourceDefinition.template.equals("template02.ftl"));
         Validate.isTrue(outputGenerators.get(1).dataSourceDefinition.dataSources.size() == 2);
         Validate.isTrue(outputGenerators.get(1).dataSourceDefinition.dataSources.get(0).equals("datasource20.csv"));
         Validate.isTrue(outputGenerators.get(1).dataSourceDefinition.dataSources.get(1).equals("datasource21.csv"));
-        Validate.isTrue(outputGenerators.get(0).outputDefinition == null);
+        Validate.isTrue(outputGenerators.get(0).templateOutputDefinition == null);
 
-        Validate.isTrue(outputGenerators.get(2).templateDefinition.interactiveTemplate.equals("some-interactive-template01"));
+        Validate.isTrue(outputGenerators.get(2).templateSourceDefinition.interactiveTemplate.equals("some-interactive-template01"));
         Validate.isTrue(outputGenerators.get(2).dataSourceDefinition.dataSources.size() == 1);
         Validate.isTrue(outputGenerators.get(2).dataSourceDefinition.dataSources.get(0).equals("datasource30.csv"));
-        Validate.isTrue(outputGenerators.get(2).outputDefinition.outputs.get(0).equals("out.txt"));
+        Validate.isTrue(outputGenerators.get(2).templateOutputDefinition.outputs.get(0).equals("out.txt"));
 
-        Validate.isTrue(outputGenerators.get(3).templateDefinition.interactiveTemplate.equals("some-interactive-template02"));
+        Validate.isTrue(outputGenerators.get(3).templateSourceDefinition.interactiveTemplate.equals("some-interactive-template02"));
 
         return;
     }
diff --git a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplierTest.java b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplierTest.java
index 93ddfa2..ac817f6 100644
--- a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplierTest.java
+++ b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplierTest.java
@@ -60,7 +60,6 @@ public class ConfigurationSupplierTest {
 
     private SettingsBuilder settingsBuilder() {
         return Settings.builder()
-                .setTemplateNames(singletonList(ANY_TEMPLATE_NAME))
-                .setWriter(new StringWriter());
+                .setCallerSuppliedWriter(new StringWriter());
     }
 }
diff --git a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SettingsTest.java b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SettingsTest.java
index cbd838a..7a7d647 100644
--- a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SettingsTest.java
+++ b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SettingsTest.java
@@ -16,20 +16,12 @@
  */
 package org.apache.freemarker.generator.cli.config;
 
-import org.apache.freemarker.generator.cli.config.Settings.SettingsBuilder;
-import org.junit.Test;
-
-import java.io.StringWriter;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
 import static java.util.Collections.singletonList;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 
 public class SettingsTest {
 
@@ -46,41 +38,44 @@ public class SettingsTest {
     private static final Map<String, Object> ANY_USER_PARAMETERS = new HashMap<>();
     private static final Properties ANY_SYSTEM_PROPERTIES = new Properties();
 
-    @Test
-    public void shouldProvideAllExpectedSettings() {
-        final Settings settings = allSettingsBuilder().build();
+    // @TODO FREEMARKER-161 sgoeschl Update the tests
+    /**
+     @Test public void shouldProvideAllExpectedSettings() {
+     final Settings settings = allSettingsBuilder().build();
+
+     assertEquals(1, settings.getCommandLineArgs().size());
+     assertNotNull(settings.getConfiguration());
+     assertEquals(ANY_INCLUDE, settings.getDataSourceIncludePattern());
+     assertEquals(ANY_INPUT_ENCODING, settings.getInputEncoding().name());
+     assertEquals(ANY_OUTPUT_ENCODING, settings.getOutputEncoding().name());
+     assertEquals(ANY_OUTPUT_FILE, settings.getOutputs().get(0));
+     assertEquals(ANY_TEMPLATE_NAME, settings.getTemplates().get(0));
+     assertNotNull(settings.getDataSources());
+     assertNotNull(settings.getUserParameters());
+     assertNotNull(settings.getUserSystemProperties());
+     assertTrue(settings.isReadFromStdin());
+     assertTrue(settings.isInteractiveTemplate());
+     assertTrue(settings.isVerbose());
+     }
 
-        assertEquals(1, settings.getArgs().size());
-        assertNotNull(settings.getConfiguration());
-        assertEquals(ANY_INCLUDE, settings.getDataSourceIncludePattern());
-        assertEquals(ANY_INPUT_ENCODING, settings.getInputEncoding().name());
-        assertEquals(ANY_OUTPUT_ENCODING, settings.getOutputEncoding().name());
-        assertEquals(ANY_OUTPUT_FILE, settings.getOutputs().get(0));
-        assertEquals(ANY_TEMPLATE_NAME, settings.getTemplates().get(0));
-        assertNotNull(settings.getDataSources());
-        assertNotNull(settings.getUserParameters());
-        assertNotNull(settings.getUserSystemProperties());
-        assertTrue(settings.isReadFromStdin());
-        assertTrue(settings.isInteractiveTemplate());
-        assertTrue(settings.isVerbose());
-    }
+     private SettingsBuilder allSettingsBuilder() {
+     return Settings.builder()
+     .isReadFromStdin(true)
+     .setCommandLineArgs(ANY_ARGS)
+     .setConfiguration(ANY_CONFIGURATION)
+     .setDataSourceIncludePattern(ANY_INCLUDE)
+     .setInputEncoding(ANY_INPUT_ENCODING)
+     .setInteractiveTemplate(ANY_INTERACTIVE_TEMPLATE)
+     .setLocale(ANY_LOCALE)
+     .setOutputEncoding(ANY_OUTPUT_ENCODING)
+     .setOutputs(Collections.singletonList(ANY_OUTPUT_FILE))
+     .setParameters(ANY_USER_PARAMETERS)
+     .setDataSources(ANY_SOURCES)
+     .setSystemProperties(ANY_SYSTEM_PROPERTIES)
+     .setTemplateNames(singletonList(ANY_TEMPLATE_NAME))
+     .setWriter(new StringWriter())
+     .setVerbose(true);
+     }
 
-    private SettingsBuilder allSettingsBuilder() {
-        return Settings.builder()
-                .isReadFromStdin(true)
-                .setArgs(ANY_ARGS)
-                .setConfiguration(ANY_CONFIGURATION)
-                .setDataSourceIncludePattern(ANY_INCLUDE)
-                .setInputEncoding(ANY_INPUT_ENCODING)
-                .setInteractiveTemplate(ANY_INTERACTIVE_TEMPLATE)
-                .setLocale(ANY_LOCALE)
-                .setOutputEncoding(ANY_OUTPUT_ENCODING)
-                .setOutputs(Collections.singletonList(ANY_OUTPUT_FILE))
-                .setParameters(ANY_USER_PARAMETERS)
-                .setDataSources(ANY_SOURCES)
-                .setSystemProperties(ANY_SYSTEM_PROPERTIES)
-                .setTemplateNames(singletonList(ANY_TEMPLATE_NAME))
-                .setWriter(new StringWriter())
-                .setVerbose(true);
-    }
+     */
 }


[freemarker-generator] 05/06: FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI

Posted by sg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a commit to branch FREEMARKER-161
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git

commit d7d9639b57399cb841109505a33fba0045bd0b37
Author: Siegfried Goeschl <si...@gmail.com>
AuthorDate: Sat Dec 26 21:57:02 2020 +0100

    FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI
---
 .../base/template/TemplateSourceFactory.java       | 40 ----------
 .../template/TemplateTransformationsBuilder.java   | 38 +++++++---
 .../template/TemplateSourceFactoryTest.java        | 88 ----------------------
 .../TemplateTransformationsBuilderTest.java        | 48 +++++++++++-
 4 files changed, 72 insertions(+), 142 deletions(-)

diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSourceFactory.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSourceFactory.java
deleted file mode 100644
index ab53ee3..0000000
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSourceFactory.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.freemarker.generator.base.template;
-
-import org.apache.freemarker.generator.base.datasource.DataSource;
-import org.apache.freemarker.generator.base.datasource.DataSourceFactory;
-import org.apache.freemarker.generator.base.util.UriUtils;
-
-import java.io.File;
-
-public abstract class TemplateSourceFactory {
-
-    public static TemplateSource create(String str) {
-        if (isTemplatePath(str)) {
-            return TemplateSource.fromPath(str);
-        } else {
-            try (DataSource dataSource = DataSourceFactory.create(str)) {
-                return TemplateSource.fromCode(dataSource.getName(), dataSource.getText());
-            }
-        }
-    }
-
-    private static boolean isTemplatePath(String str) {
-        return !UriUtils.isUri(str) && !new File(str).exists();
-    }
-}
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java
index 261e3f4..e352173 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java
@@ -17,6 +17,8 @@
 package org.apache.freemarker.generator.base.template;
 
 import org.apache.freemarker.generator.base.FreeMarkerConstants.Location;
+import org.apache.freemarker.generator.base.datasource.DataSource;
+import org.apache.freemarker.generator.base.datasource.DataSourceFactory;
 import org.apache.freemarker.generator.base.file.RecursiveFileSupplier;
 import org.apache.freemarker.generator.base.util.StringUtils;
 import org.apache.freemarker.generator.base.util.Validate;
@@ -154,10 +156,12 @@ public class TemplateTransformationsBuilder {
             return resolveTemplateFile(source, output);
         } else if (isTemplateDirectoryFound(source)) {
             return resolveTemplateDirectory(source, output);
-        } else if (isTemplatePath(source)) {
-            return resolveTemplatePath(source, output);
+        } else if (isTemplateHttpUrl(source)) {
+            return resolveTemplateHttpUrl(source, output);
+        } else if (isTemplateUri(source)) {
+            return resolveTemplateUri(source, output);
         } else {
-            return resolveTemplateCode(source, output);
+            return resolveTemplatePath(source, output);
         }
     }
 
@@ -184,23 +188,29 @@ public class TemplateTransformationsBuilder {
         return templateTransformations;
     }
 
-    private List<TemplateTransformation> resolveTemplatePath(String source, File out) {
+    private List<TemplateTransformation> resolveTemplateHttpUrl(String source, File out) {
         final TemplateSource templateSource = templateSource(source);
         final TemplateOutput templateOutput = templateOutput(out);
         return singletonList(new TemplateTransformation(templateSource, templateOutput));
     }
 
-    private TemplateTransformation resolveInteractiveTemplate(File out) {
+    private List<TemplateTransformation> resolveTemplateUri(String source, File out) {
+        final TemplateSource templateSource = templateSource(source);
         final TemplateOutput templateOutput = templateOutput(out);
-        return new TemplateTransformation(interactiveTemplate, templateOutput);
+        return singletonList(new TemplateTransformation(templateSource, templateOutput));
     }
 
-    private List<TemplateTransformation> resolveTemplateCode(String source, File out) {
-        final TemplateSource templateSource = TemplateSource.fromCode(Location.INTERACTIVE, source);
+    private List<TemplateTransformation> resolveTemplatePath(String source, File out) {
+        final TemplateSource templateSource = TemplateSource.fromPath(source);
         final TemplateOutput templateOutput = templateOutput(out);
         return singletonList(new TemplateTransformation(templateSource, templateOutput));
     }
 
+    private TemplateTransformation resolveInteractiveTemplate(File out) {
+        final TemplateOutput templateOutput = templateOutput(out);
+        return new TemplateTransformation(interactiveTemplate, templateOutput);
+    }
+
     private TemplateOutput templateOutput(File templateOutputFile) {
         if (writer == null && templateOutputFile != null) {
             return TemplateOutput.fromFile(templateOutputFile);
@@ -210,7 +220,9 @@ public class TemplateTransformationsBuilder {
     }
 
     private TemplateSource templateSource(String source) {
-        return TemplateSourceFactory.create(source);
+        try (DataSource dataSource = DataSourceFactory.create(source)) {
+            return TemplateSource.fromCode(dataSource.getName(), dataSource.getText());
+        }
     }
 
     private String getInclude() {
@@ -251,8 +263,12 @@ public class TemplateTransformationsBuilder {
         return file.exists() && file.isDirectory();
     }
 
-    private static boolean isTemplatePath(String source) {
-        return !isTemplateFileFound(source) && !isTemplateDirectoryFound(source);
+    private static boolean isTemplateHttpUrl(String source) {
+        return source.contains("http://") || source.contains("https://");
+    }
+
+    private static boolean isTemplateUri(String source) {
+        return source.contains("://");
     }
 
     private static RecursiveFileSupplier templateFilesSupplier(String source, String include, String exclude) {
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateSourceFactoryTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateSourceFactoryTest.java
deleted file mode 100644
index 2088e4b..0000000
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateSourceFactoryTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.freemarker.generator.template;
-
-import org.apache.freemarker.generator.base.template.TemplateSource;
-import org.apache.freemarker.generator.base.template.TemplateSource.Origin;
-import org.apache.freemarker.generator.base.template.TemplateSourceFactory;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-public class TemplateSourceFactoryTest {
-
-    private static final String ANY_TEMPLATE_PATH = "any/template/path.ftl";
-    private static final String ANY_FILE_NAME = "pom.xml";
-    private static final String ANY_URL = "https://raw.githubusercontent.com/apache/freemarker-generator/master/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl";
-    private static final String ANY_ENV_VARIABLE = "JAVA_HOME";
-    private static final String ANY_ENV_URI = "env:///" + ANY_ENV_VARIABLE;
-
-    @Test
-    public void shouldCreateFromTemplatePath() {
-        final TemplateSource templateSource = TemplateSourceFactory.create(ANY_TEMPLATE_PATH);
-
-        assertEquals(ANY_TEMPLATE_PATH, templateSource.getName());
-        assertEquals(Origin.TEMPLATE_LOADER, templateSource.getOrigin());
-        assertEquals(ANY_TEMPLATE_PATH, templateSource.getPath());
-        assertNull(templateSource.getCode());
-    }
-
-    @Test
-    public void shouldCreateFromFile() {
-        final TemplateSource templateSource = TemplateSourceFactory.create(ANY_FILE_NAME);
-
-        assertEquals(ANY_FILE_NAME, templateSource.getName());
-        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
-        assertNull(templateSource.getPath());
-        assertFalse(templateSource.getCode().isEmpty());
-    }
-
-    @Test
-    public void shouldCreateFromEnvironmentVariable() {
-        final TemplateSource templateSource = TemplateSourceFactory.create(ANY_ENV_URI);
-
-        assertEquals(ANY_ENV_VARIABLE, templateSource.getName());
-        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
-        assertNull(templateSource.getPath());
-        assertFalse(templateSource.getCode().isEmpty());
-    }
-
-    @Test
-    // @Ignore("Requires internet access")
-    public void shouldCreateFromUrl() {
-        final TemplateSource templateSource = TemplateSourceFactory.create(ANY_URL);
-
-        assertNotNull(templateSource.getName());
-        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
-        assertNull(templateSource.getPath());
-        assertFalse(templateSource.getCode().isEmpty());
-    }
-
-    @Test
-    // @Ignore("Requires internet access")
-    public void shouldCreateFromNamedUri() {
-        final TemplateSource templateSource = TemplateSourceFactory.create("info=" + ANY_URL);
-
-        assertEquals("info", templateSource.getName());
-        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
-        assertNull(templateSource.getPath());
-        assertFalse(templateSource.getCode().isEmpty());
-    }
-}
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java
index 1d86574..66d3c9a 100644
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java
+++ b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java
@@ -34,8 +34,10 @@ import java.util.List;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 public class TemplateTransformationsBuilderTest {
 
@@ -43,6 +45,8 @@ public class TemplateTransformationsBuilderTest {
     private static final String OTHER_TEMPLATE_FILE_NAME = "src/test/template/nginx/nginx.conf.ftl";
     private static final String ANY_TEMPLATE_PATH = "template/info.ftl";
     private static final String ANY_TEMPLATE_DIRECTORY_NAME = "src/test/template";
+    private static final String ANY_TEMPLATE_URL = "https://raw.githubusercontent.com/apache/freemarker-generator/master/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl";
+    private static final String ANY_ENV_URI = "env:///JAVA_HOME";
 
     // === Interactive Template =============================================
 
@@ -93,7 +97,7 @@ public class TemplateTransformationsBuilderTest {
 
         assertNotNull(templateSource.getName());
         assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
-        assertNotNull(templateSource.getCode());
+        assertTrue(templateSource.getCode().contains("Licensed to the Apache Software Foundation"));
         assertNull(templateSource.getPath());
         assertEquals(StandardCharsets.UTF_8, templateSource.getEncoding());
 
@@ -129,10 +133,10 @@ public class TemplateTransformationsBuilderTest {
         final TemplateSource templateSource = transformations.get(0).getTemplateSource();
         final TemplateOutput templateOutput = transformations.get(0).getTemplateOutput();
 
-        assertNotNull(templateSource.getName());
+        assertEquals(ANY_TEMPLATE_PATH, templateSource.getName());
         assertEquals(Origin.TEMPLATE_LOADER, templateSource.getOrigin());
         assertNull(templateSource.getCode());
-        assertNotNull(templateSource.getPath());
+        assertEquals(ANY_TEMPLATE_PATH, templateSource.getPath());
         assertEquals(StandardCharsets.UTF_8, templateSource.getEncoding());
 
         assertNotNull(templateOutput.getWriter());
@@ -189,6 +193,44 @@ public class TemplateTransformationsBuilderTest {
         assertEquals("application.properties", transformations.get(0).getTemplateSource().getName());
     }
 
+    // === Template URL ===============================================
+
+    @Test
+    public void shouldCreateFromTemplateUrl() {
+        final List<TemplateTransformation> transformations = builder()
+                .addTemplateSource(ANY_TEMPLATE_URL)
+                .setWriter(stdoutWriter())
+                .build();
+
+        final TemplateSource templateSource = transformations.get(0).getTemplateSource();
+
+        assertEquals(1, transformations.size());
+        assertEquals(ANY_TEMPLATE_URL, templateSource.getName());
+        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
+        assertNull(templateSource.getPath());
+        assertEquals(StandardCharsets.UTF_8, templateSource.getEncoding());
+        assertTrue(templateSource.getCode().contains("<#ftl"));
+    }
+
+    // === Template ENV Variable =============================================
+
+    @Test
+    public void shouldCreateFromTemplateEnvironmentVariable() {
+        final List<TemplateTransformation> transformations = builder()
+                .addTemplateSource(ANY_ENV_URI)
+                .setWriter(stdoutWriter())
+                .build();
+
+        final TemplateSource templateSource = transformations.get(0).getTemplateSource();
+
+        assertEquals(1, transformations.size());
+        assertEquals("JAVA_HOME", templateSource.getName());
+        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
+        assertNull(templateSource.getPath());
+        assertEquals(StandardCharsets.UTF_8, templateSource.getEncoding());
+        assertFalse(templateSource.getCode().isEmpty());
+    }
+
     private TemplateTransformationsBuilder builder() {
         return TemplateTransformationsBuilder
                 .builder();


[freemarker-generator] 02/06: Merge branch 'master' into FREEMARKER-161

Posted by sg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a commit to branch FREEMARKER-161
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git

commit 68df50cad0117b5dabf8e92bcc3842d2b8c7b600
Merge: e73f23d d32e630
Author: Siegfried Goeschl <si...@gmail.com>
AuthorDate: Tue Dec 22 13:23:05 2020 +0100

    Merge branch 'master' into FREEMARKER-161

 freemarker-generator-cli/CHANGELOG.md              |  7 ++++-
 .../cli/config/ConfigurationSupplier.java          |  5 ---
 .../generator/cli/SecurityRelatedTest.java         | 36 ----------------------
 .../src/test/templates/security/api.ftl            | 17 ----------
 .../src/test/templates/security/new.ftl            | 17 ----------
 freemarker-generator-tools/pom.xml                 |  8 ++++-
 .../generator/tools/commonscsv/CommonsCSVTool.java |  6 ++++
 .../generator/tools/excel/ExcelTool.java           |  8 +++++
 .../freemarker/generator/tools/gson/GsonTool.java  | 15 +++++++++
 .../generator/tools/jsonpath/JsonPathTool.java     | 13 +++++++-
 .../generator/tools/properties/PropertiesTool.java |  9 ++++++
 .../generator/tools/snakeyaml/SnakeYamlTool.java   |  9 ++++++
 .../freemarker/generator/tools/xml/XmlTool.java    |  9 ++++++
 .../generator/tools/gson/GsonToolTest.java         | 29 ++++++++++++++++-
 .../generator/tools/jsoup/JsoupToolTest.java       |  2 --
 15 files changed, 109 insertions(+), 81 deletions(-)


[freemarker-generator] 04/06: FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI

Posted by sg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a commit to branch FREEMARKER-161
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git

commit 5f89acd1df8c90a8a330d469807572c28003d0a4
Author: Siegfried Goeschl <si...@gmail.com>
AuthorDate: Sat Dec 26 10:02:26 2020 +0100

    FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI
---
 .../generator/base/template/TemplateSource.java    |  8 +--
 .../base/template/TemplateTransformations.java     | 47 --------------
 .../template/TemplateTransformationsBuilder.java   | 72 ++++++++--------------
 .../template/TemplateTransformationsSupplier.java  | 22 -------
 .../template/TemplateSourceFactoryTest.java        | 10 +--
 .../TemplateTransformationsBuilderTest.java        | 71 +++++++++++----------
 .../freemarker/generator/cli/config/Suppliers.java | 10 +--
 .../generator/cli/task/FreeMarkerTask.java         | 17 +++--
 8 files changed, 87 insertions(+), 170 deletions(-)

diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSource.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSource.java
index fb50b2a..5e57489 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSource.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateSource.java
@@ -29,8 +29,8 @@ import java.nio.charset.StandardCharsets;
 public class TemplateSource {
 
     public enum Origin {
-        PATH,
-        CODE
+        TEMPLATE_LOADER,
+        TEMPLATE_CODE
     }
 
     /** Name of template for diagnostics */
@@ -50,7 +50,7 @@ public class TemplateSource {
 
     private TemplateSource(String name, String code) {
         this.name = name;
-        this.origin = Origin.CODE;
+        this.origin = Origin.TEMPLATE_CODE;
         this.code = code;
         this.path = null;
         this.encoding = StandardCharsets.UTF_8;
@@ -58,7 +58,7 @@ public class TemplateSource {
 
     private TemplateSource(String name, String path, Charset encoding) {
         this.name = name;
-        this.origin = Origin.PATH;
+        this.origin = Origin.TEMPLATE_LOADER;
         this.code = null;
         this.path = path;
         this.encoding = encoding;
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformations.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformations.java
deleted file mode 100644
index e41162d..0000000
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformations.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.freemarker.generator.base.template;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * Keeps track of all transformations being executed.
- */
-public class TemplateTransformations {
-
-    private final List<TemplateTransformation> templateTransformations;
-
-    public TemplateTransformations(Collection<? extends TemplateTransformation> templateTransformations) {
-        this.templateTransformations = new ArrayList<>(requireNonNull(templateTransformations));
-    }
-
-    public List<? extends TemplateTransformation> getList() {
-        return templateTransformations;
-    }
-
-    public TemplateTransformation get(int index) {
-        return templateTransformations.get(index);
-    }
-
-    public int size() {
-        return templateTransformations.size();
-    }
-}
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java
index bdef609..261e3f4 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsBuilder.java
@@ -18,32 +18,29 @@ package org.apache.freemarker.generator.base.template;
 
 import org.apache.freemarker.generator.base.FreeMarkerConstants.Location;
 import org.apache.freemarker.generator.base.file.RecursiveFileSupplier;
-import org.apache.freemarker.generator.base.util.NonClosableWriterWrapper;
 import org.apache.freemarker.generator.base.util.StringUtils;
 import org.apache.freemarker.generator.base.util.Validate;
 
-import java.io.BufferedWriter;
 import java.io.File;
-import java.io.OutputStreamWriter;
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Collections.singletonList;
 
 /**
- * Provide the logic to define multiple transformations from the user input.
+ * Maps user-supplied templates (interactive, files, directories, paths) to a
+ * list of TemplateTransformation.
  */
 public class TemplateTransformationsBuilder {
 
     /** Interactive template */
-    private TemplateSource template;
+    private TemplateSource interactiveTemplate;
 
     /** List of templates and/or template directories to be rendered */
-    private final List<String> sources;
+    private final List<String> templateSources;
 
     /** Optional include patterns for resolving source templates or template directories */
     private final List<String> includes;
@@ -51,14 +48,14 @@ public class TemplateTransformationsBuilder {
     /** Optional exclude patterns for resolving source templates or template directories */
     private final List<String> excludes;
 
-    /** Optional output file or directory */
+    /** Optional output file(s) or directory - if none is defined everything is written to a user-supplied writer */
     private final List<String> outputs;
 
     /** Optional user-supplied writer */
     private Writer writer;
 
     private TemplateTransformationsBuilder() {
-        this.sources = new ArrayList<>();
+        this.templateSources = new ArrayList<>();
         this.includes = new ArrayList<>();
         this.excludes = new ArrayList<>();
         this.outputs = new ArrayList<>();
@@ -69,7 +66,7 @@ public class TemplateTransformationsBuilder {
         return new TemplateTransformationsBuilder();
     }
 
-    public TemplateTransformations build() {
+    public List<TemplateTransformation> build() {
         validate();
 
         final List<TemplateTransformation> result = new ArrayList<>();
@@ -78,32 +75,32 @@ public class TemplateTransformationsBuilder {
             final File outputFile = getOutputFile(0).orElse(null);
             result.add(resolveInteractiveTemplate(outputFile));
         } else {
-            for (int i = 0; i < sources.size(); i++) {
-                final String source = sources.get(i);
+            for (int i = 0; i < templateSources.size(); i++) {
+                final String source = templateSources.get(i);
                 final File output = getOutputFile(i).orElse(null);
                 result.addAll(resolve(source, output));
             }
         }
 
-        return new TemplateTransformations(result);
+        return result;
     }
 
-    public TemplateTransformationsBuilder setTemplate(String name, String code) {
+    public TemplateTransformationsBuilder setInteractiveTemplate(String code) {
         if (StringUtils.isNotEmpty(code)) {
-            this.template = TemplateSource.fromCode(name, code);
+            this.interactiveTemplate = TemplateSource.fromCode(Location.INTERACTIVE, code);
         }
         return this;
     }
 
-    public TemplateTransformationsBuilder addSource(String source) {
+    public TemplateTransformationsBuilder addTemplateSource(String source) {
         if (StringUtils.isNotEmpty(source)) {
-            this.sources.add(source);
+            this.templateSources.add(source);
         }
         return this;
     }
 
-    public TemplateTransformationsBuilder addSources(Collection<String> sources) {
-        sources.forEach(this::addSource);
+    public TemplateTransformationsBuilder addTemplateSources(Collection<String> sources) {
+        sources.forEach(this::addTemplateSource);
         return this;
     }
 
@@ -114,13 +111,6 @@ public class TemplateTransformationsBuilder {
         return this;
     }
 
-    public TemplateTransformationsBuilder addIncludes(Collection<String> includes) {
-        if (includes != null) {
-            this.includes.addAll(includes);
-        }
-        return this;
-    }
-
     public TemplateTransformationsBuilder addExclude(String exclude) {
         if (StringUtils.isNotEmpty(exclude)) {
             this.excludes.add(exclude);
@@ -128,13 +118,6 @@ public class TemplateTransformationsBuilder {
         return this;
     }
 
-    public TemplateTransformationsBuilder addExcludes(Collection<String> excludes) {
-        if (excludes != null) {
-            this.excludes.addAll(excludes);
-        }
-        return this;
-    }
-
     public TemplateTransformationsBuilder addOutputs(Collection<String> outputs) {
         if (outputs != null) {
             this.outputs.addAll(outputs);
@@ -154,14 +137,9 @@ public class TemplateTransformationsBuilder {
         return this;
     }
 
-    public TemplateTransformationsBuilder setStdOut() {
-        this.writer = new NonClosableWriterWrapper(new BufferedWriter(new OutputStreamWriter(System.out, UTF_8)));
-        return this;
-    }
-
     private void validate() {
-        Validate.isTrue(template != null || !sources.isEmpty(), "No template was provided");
-        Validate.isTrue(template == null || sources.isEmpty(), "Interactive template does not support multiple sources");
+        Validate.isTrue(interactiveTemplate != null || !templateSources.isEmpty(), "Interactive template does not support multiple sources");
+        Validate.isTrue(interactiveTemplate == null || templateSources.isEmpty(), "No template was provided");
     }
 
     /**
@@ -172,9 +150,9 @@ public class TemplateTransformationsBuilder {
      * @return list of <code>TemplateTransformation</code>
      */
     private List<TemplateTransformation> resolve(String source, File output) {
-        if (isTemplateFile(source)) {
+        if (isTemplateFileFound(source)) {
             return resolveTemplateFile(source, output);
-        } else if (isTemplateDirectory(source)) {
+        } else if (isTemplateDirectoryFound(source)) {
             return resolveTemplateDirectory(source, output);
         } else if (isTemplatePath(source)) {
             return resolveTemplatePath(source, output);
@@ -214,7 +192,7 @@ public class TemplateTransformationsBuilder {
 
     private TemplateTransformation resolveInteractiveTemplate(File out) {
         final TemplateOutput templateOutput = templateOutput(out);
-        return new TemplateTransformation(template, templateOutput);
+        return new TemplateTransformation(interactiveTemplate, templateOutput);
     }
 
     private List<TemplateTransformation> resolveTemplateCode(String source, File out) {
@@ -244,7 +222,7 @@ public class TemplateTransformationsBuilder {
     }
 
     private boolean hasInteractiveTemplate() {
-        return template != null;
+        return interactiveTemplate != null;
     }
 
     private Optional<File> getOutputFile(int i) {
@@ -263,18 +241,18 @@ public class TemplateTransformationsBuilder {
         return new File(outputDirectory, relativeOutputFileName);
     }
 
-    private static boolean isTemplateFile(String source) {
+    private static boolean isTemplateFileFound(String source) {
         final File file = new File(source);
         return file.exists() && file.isFile();
     }
 
-    private static boolean isTemplateDirectory(String source) {
+    private static boolean isTemplateDirectoryFound(String source) {
         final File file = new File(source);
         return file.exists() && file.isDirectory();
     }
 
     private static boolean isTemplatePath(String source) {
-        return !isTemplateFile(source) && !isTemplateDirectory(source);
+        return !isTemplateFileFound(source) && !isTemplateDirectoryFound(source);
     }
 
     private static RecursiveFileSupplier templateFilesSupplier(String source, String include, String exclude) {
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java
deleted file mode 100644
index ef06672..0000000
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.freemarker.generator.base.template;
-
-import java.util.function.Supplier;
-
-public interface TemplateTransformationsSupplier extends Supplier<TemplateTransformations> {
-}
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateSourceFactoryTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateSourceFactoryTest.java
index 0124a98..2088e4b 100644
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateSourceFactoryTest.java
+++ b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateSourceFactoryTest.java
@@ -39,7 +39,7 @@ public class TemplateSourceFactoryTest {
         final TemplateSource templateSource = TemplateSourceFactory.create(ANY_TEMPLATE_PATH);
 
         assertEquals(ANY_TEMPLATE_PATH, templateSource.getName());
-        assertEquals(Origin.PATH, templateSource.getOrigin());
+        assertEquals(Origin.TEMPLATE_LOADER, templateSource.getOrigin());
         assertEquals(ANY_TEMPLATE_PATH, templateSource.getPath());
         assertNull(templateSource.getCode());
     }
@@ -49,7 +49,7 @@ public class TemplateSourceFactoryTest {
         final TemplateSource templateSource = TemplateSourceFactory.create(ANY_FILE_NAME);
 
         assertEquals(ANY_FILE_NAME, templateSource.getName());
-        assertEquals(Origin.CODE, templateSource.getOrigin());
+        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
         assertNull(templateSource.getPath());
         assertFalse(templateSource.getCode().isEmpty());
     }
@@ -59,7 +59,7 @@ public class TemplateSourceFactoryTest {
         final TemplateSource templateSource = TemplateSourceFactory.create(ANY_ENV_URI);
 
         assertEquals(ANY_ENV_VARIABLE, templateSource.getName());
-        assertEquals(Origin.CODE, templateSource.getOrigin());
+        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
         assertNull(templateSource.getPath());
         assertFalse(templateSource.getCode().isEmpty());
     }
@@ -70,7 +70,7 @@ public class TemplateSourceFactoryTest {
         final TemplateSource templateSource = TemplateSourceFactory.create(ANY_URL);
 
         assertNotNull(templateSource.getName());
-        assertEquals(Origin.CODE, templateSource.getOrigin());
+        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
         assertNull(templateSource.getPath());
         assertFalse(templateSource.getCode().isEmpty());
     }
@@ -81,7 +81,7 @@ public class TemplateSourceFactoryTest {
         final TemplateSource templateSource = TemplateSourceFactory.create("info=" + ANY_URL);
 
         assertEquals("info", templateSource.getName());
-        assertEquals(Origin.CODE, templateSource.getOrigin());
+        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
         assertNull(templateSource.getPath());
         assertFalse(templateSource.getCode().isEmpty());
     }
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java
index 0c082a0..1d86574 100644
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java
+++ b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/template/TemplateTransformationsBuilderTest.java
@@ -20,13 +20,19 @@ import org.apache.freemarker.generator.base.FreeMarkerConstants.Location;
 import org.apache.freemarker.generator.base.template.TemplateOutput;
 import org.apache.freemarker.generator.base.template.TemplateSource;
 import org.apache.freemarker.generator.base.template.TemplateSource.Origin;
-import org.apache.freemarker.generator.base.template.TemplateTransformations;
+import org.apache.freemarker.generator.base.template.TemplateTransformation;
 import org.apache.freemarker.generator.base.template.TemplateTransformationsBuilder;
+import org.apache.freemarker.generator.base.util.NonClosableWriterWrapper;
 import org.junit.Test;
 
+import java.io.BufferedWriter;
 import java.io.File;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
 import java.nio.charset.StandardCharsets;
+import java.util.List;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -42,9 +48,9 @@ public class TemplateTransformationsBuilderTest {
 
     @Test
     public void shouldCreateFromInteractiveTemplate() {
-        final TemplateTransformations transformations = builder()
-                .setTemplate(Location.INTERACTIVE, "Hello World")
-                .setStdOut()
+        final List<TemplateTransformation> transformations = builder()
+                .setInteractiveTemplate("Hello World")
+                .setWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -53,7 +59,7 @@ public class TemplateTransformationsBuilderTest {
         final TemplateOutput templateOutput = transformations.get(0).getTemplateOutput();
 
         assertEquals(Location.INTERACTIVE, templateSource.getName());
-        assertEquals(Origin.CODE, templateSource.getOrigin());
+        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
         assertEquals("Hello World", templateSource.getCode());
         assertNull(templateSource.getPath());
         assertEquals(StandardCharsets.UTF_8, templateSource.getEncoding());
@@ -65,20 +71,19 @@ public class TemplateTransformationsBuilderTest {
     @Test(expected = IllegalArgumentException.class)
     public void shouldThrowIllegalArgumentExceptionWheMixingInteractiveTemplateWithSources() {
         builder()
-                .setTemplate(Location.INTERACTIVE, "Hello World")
-                .addSource(ANY_TEMPLATE_FILE_NAME)
-                .setStdOut()
+                .setInteractiveTemplate("Hello World")
+                .addTemplateSource(ANY_TEMPLATE_FILE_NAME)
+                .setWriter(stdoutWriter())
                 .build();
     }
 
-
     // === Template File ====================================================
 
     @Test
     public void shouldCreateFromTemplateFile() {
-        final TemplateTransformations transformations = builder()
-                .addSource(ANY_TEMPLATE_FILE_NAME)
-                .setStdOut()
+        final List<TemplateTransformation> transformations = builder()
+                .addTemplateSource(ANY_TEMPLATE_FILE_NAME)
+                .setWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -87,7 +92,7 @@ public class TemplateTransformationsBuilderTest {
         final TemplateOutput templateOutput = transformations.get(0).getTemplateOutput();
 
         assertNotNull(templateSource.getName());
-        assertEquals(Origin.CODE, templateSource.getOrigin());
+        assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
         assertNotNull(templateSource.getCode());
         assertNull(templateSource.getPath());
         assertEquals(StandardCharsets.UTF_8, templateSource.getEncoding());
@@ -98,10 +103,10 @@ public class TemplateTransformationsBuilderTest {
 
     @Test
     public void shouldCreateFromMultipleTemplateFiles() {
-        final TemplateTransformations transformations = builder()
-                .addSource(ANY_TEMPLATE_FILE_NAME)
+        final List<TemplateTransformation> transformations = builder()
+                .addTemplateSource(ANY_TEMPLATE_FILE_NAME)
                 .addOutput("foo/first.out")
-                .addSource(OTHER_TEMPLATE_FILE_NAME)
+                .addTemplateSource(OTHER_TEMPLATE_FILE_NAME)
                 .addOutput("foo/second.out")
                 .build();
 
@@ -114,9 +119,9 @@ public class TemplateTransformationsBuilderTest {
 
     @Test
     public void shouldCreateFromTemplatePath() {
-        final TemplateTransformations transformations = builder()
-                .addSource(ANY_TEMPLATE_PATH)
-                .setStdOut()
+        final List<TemplateTransformation> transformations = builder()
+                .addTemplateSource(ANY_TEMPLATE_PATH)
+                .setWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -125,7 +130,7 @@ public class TemplateTransformationsBuilderTest {
         final TemplateOutput templateOutput = transformations.get(0).getTemplateOutput();
 
         assertNotNull(templateSource.getName());
-        assertEquals(Origin.PATH, templateSource.getOrigin());
+        assertEquals(Origin.TEMPLATE_LOADER, templateSource.getOrigin());
         assertNull(templateSource.getCode());
         assertNotNull(templateSource.getPath());
         assertEquals(StandardCharsets.UTF_8, templateSource.getEncoding());
@@ -138,9 +143,9 @@ public class TemplateTransformationsBuilderTest {
 
     @Test
     public void shouldCreateFromTemplateDirectory() {
-        final TemplateTransformations transformations = builder()
-                .addSource(ANY_TEMPLATE_DIRECTORY_NAME)
-                .setStdOut()
+        final List<TemplateTransformation> transformations = builder()
+                .addTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
+                .setWriter(stdoutWriter())
                 .build();
 
         assertEquals(2, transformations.size());
@@ -150,8 +155,8 @@ public class TemplateTransformationsBuilderTest {
 
     @Test
     public void shouldCreateFromTemplateDirectoryWithOutputDirectory() {
-        final TemplateTransformations transformations = builder()
-                .addSource(ANY_TEMPLATE_DIRECTORY_NAME)
+        final List<TemplateTransformation> transformations = builder()
+                .addTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
                 .addOutput("/foo")
                 .build();
 
@@ -162,10 +167,10 @@ public class TemplateTransformationsBuilderTest {
 
     @Test
     public void shouldCreateFromTemplateDirectoryWithInclude() {
-        final TemplateTransformations transformations = builder()
-                .addSource(ANY_TEMPLATE_DIRECTORY_NAME)
+        final List<TemplateTransformation> transformations = builder()
+                .addTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
                 .addInclude("*.properties")
-                .setStdOut()
+                .setWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -174,10 +179,10 @@ public class TemplateTransformationsBuilderTest {
 
     @Test
     public void shouldCreateFromTemplateDirectoryWithExclude() {
-        final TemplateTransformations transformations = builder()
-                .addSource(ANY_TEMPLATE_DIRECTORY_NAME)
+        final List<TemplateTransformation> transformations = builder()
+                .addTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
                 .addExclude("*.ftl")
-                .setStdOut()
+                .setWriter(stdoutWriter())
                 .build();
 
         assertEquals(1, transformations.size());
@@ -188,4 +193,8 @@ public class TemplateTransformationsBuilderTest {
         return TemplateTransformationsBuilder
                 .builder();
     }
+
+    private Writer stdoutWriter() {
+        return new NonClosableWriterWrapper(new BufferedWriter(new OutputStreamWriter(System.out, UTF_8)));
+    }
 }
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Suppliers.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Suppliers.java
index b6e6fd0..a7bc21d 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Suppliers.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/Suppliers.java
@@ -17,14 +17,14 @@
 package org.apache.freemarker.generator.cli.config;
 
 import freemarker.cache.TemplateLoader;
-import org.apache.freemarker.generator.base.FreeMarkerConstants.Location;
 import org.apache.freemarker.generator.base.datasource.DataSourcesSupplier;
 import org.apache.freemarker.generator.base.file.PropertiesClassPathSupplier;
 import org.apache.freemarker.generator.base.file.PropertiesFileSystemSupplier;
 import org.apache.freemarker.generator.base.file.PropertiesSupplier;
+import org.apache.freemarker.generator.base.template.TemplateTransformation;
 import org.apache.freemarker.generator.base.template.TemplateTransformationsBuilder;
-import org.apache.freemarker.generator.base.template.TemplateTransformationsSupplier;
 
+import java.util.List;
 import java.util.Map;
 import java.util.function.Supplier;
 
@@ -68,10 +68,10 @@ public class Suppliers {
         return settings::getUserParameters;
     }
 
-    public static TemplateTransformationsSupplier templateTransformationsSupplier(Settings settings) {
+    public static Supplier<List<TemplateTransformation>> templateTransformationsSupplier(Settings settings) {
         return () -> TemplateTransformationsBuilder.builder()
-                .setTemplate(Location.INTERACTIVE, settings.getInteractiveTemplate())
-                .addSources(settings.getTemplates())
+                .setInteractiveTemplate(settings.getInteractiveTemplate())
+                .addTemplateSources(settings.getTemplates())
                 .addInclude(settings.getTemplateFileIncludePattern())
                 .addExclude(settings.getTemplateFileExcludePattern())
                 .addOutputs(settings.getOutputs())
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
index 3842508..cc20d76 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/task/FreeMarkerTask.java
@@ -27,7 +27,6 @@ import org.apache.freemarker.generator.base.datasource.DataSources;
 import org.apache.freemarker.generator.base.template.TemplateOutput;
 import org.apache.freemarker.generator.base.template.TemplateSource;
 import org.apache.freemarker.generator.base.template.TemplateTransformation;
-import org.apache.freemarker.generator.base.template.TemplateTransformations;
 import org.apache.freemarker.generator.base.util.UriUtils;
 import org.apache.freemarker.generator.cli.config.Settings;
 
@@ -70,7 +69,7 @@ public class FreeMarkerTask implements Callable<Integer> {
     private final Supplier<Map<String, Object>> dataModelsSupplier;
     private final Supplier<Map<String, Object>> parameterModelSupplier;
     private final Supplier<Configuration> configurationSupplier;
-    private final Supplier<TemplateTransformations> templateTransformationsSupplier;
+    private final Supplier<List<TemplateTransformation>> templateTransformationsSupplier;
 
 
     public FreeMarkerTask(Settings settings) {
@@ -86,7 +85,7 @@ public class FreeMarkerTask implements Callable<Integer> {
 
     public FreeMarkerTask(Settings settings,
                           Supplier<Configuration> configurationSupplier,
-                          Supplier<TemplateTransformations> templateTransformationsSupplier,
+                          Supplier<List<TemplateTransformation>> templateTransformationsSupplier,
                           Supplier<List<DataSource>> dataSourcesSupplier,
                           Supplier<Map<String, Object>> dataModelsSupplier,
                           Supplier<Map<String, Object>> parameterModelSupplier,
@@ -104,10 +103,10 @@ public class FreeMarkerTask implements Callable<Integer> {
     public Integer call() {
         try {
             final Configuration configuration = configurationSupplier.get();
-            final TemplateTransformations templateTransformations = templateTransformationsSupplier.get();
+            final List<TemplateTransformation> templateTransformations = templateTransformationsSupplier.get();
             final DataSources dataSources = dataSources(settings, dataSourcesSupplier);
             final Map<String, Object> dataModel = dataModel(dataSources, parameterModelSupplier, dataModelsSupplier, toolsSupplier);
-            templateTransformations.getList().forEach(t -> process(configuration, t, dataModel));
+            templateTransformations.forEach(t -> process(configuration, t, dataModel));
             return SUCCESS;
         } catch (RuntimeException e) {
             throw new RuntimeException("Failed to process templates", e);
@@ -177,16 +176,16 @@ public class FreeMarkerTask implements Callable<Integer> {
      */
     private static Template template(Configuration configuration, TemplateSource templateSource) {
         switch (templateSource.getOrigin()) {
-            case PATH:
-                return fromTemplatePath(configuration, templateSource);
-            case CODE:
+            case TEMPLATE_LOADER:
+                return fromTemplateLoader(configuration, templateSource);
+            case TEMPLATE_CODE:
                 return fromTemplateCode(configuration, templateSource);
             default:
                 throw new IllegalArgumentException("Don't know how to handle: " + templateSource.getOrigin());
         }
     }
 
-    private static Template fromTemplatePath(Configuration configuration, TemplateSource templateSource) {
+    private static Template fromTemplateLoader(Configuration configuration, TemplateSource templateSource) {
         final String path = templateSource.getPath();
         try {
             return configuration.getTemplate(path);


[freemarker-generator] 01/06: FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI

Posted by sg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a commit to branch FREEMARKER-161
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git

commit e73f23d97c913f42c1783d762dea885cd395808b
Author: Siegfried Goeschl <si...@gmail.com>
AuthorDate: Mon Nov 9 09:33:08 2020 +0100

    FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI
---
 .../base/template/TemplateTransformation.java      | 17 +++++
 .../generator/cli/config/CompositeGroupDemo.java   | 77 ++++++++++++++++++++++
 2 files changed, 94 insertions(+)

diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformation.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformation.java
index 8c19478..1d0ce7d 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformation.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformation.java
@@ -16,6 +16,8 @@
  */
 package org.apache.freemarker.generator.base.template;
 
+import org.apache.freemarker.generator.base.datasource.DataSources;
+
 import static java.util.Objects.requireNonNull;
 
 /**
@@ -26,11 +28,21 @@ public class TemplateTransformation {
     /** Source of template */
     private final TemplateSource templateSource;
 
+    /** Data sources being transformed */
+    private final DataSources dataSources;
+
     /** Output of template */
     private final TemplateOutput templateOutput;
 
     public TemplateTransformation(TemplateSource templateSource, TemplateOutput templateOutput) {
         this.templateSource = requireNonNull(templateSource);
+        this.dataSources = null;
+        this.templateOutput = requireNonNull(templateOutput);
+    }
+
+    public TemplateTransformation(TemplateSource templateSource, DataSources dataSources, TemplateOutput templateOutput) {
+        this.templateSource = requireNonNull(templateSource);
+        this.dataSources = requireNonNull(dataSources);
         this.templateOutput = requireNonNull(templateOutput);
     }
 
@@ -42,10 +54,15 @@ public class TemplateTransformation {
         return templateOutput;
     }
 
+    public DataSources getDataSources() {
+        return dataSources;
+    }
+
     @Override
     public String toString() {
         return "TemplateTransformation{" +
                 "templateSource=" + templateSource +
+                "dataSources=" + dataSources +
                 ", templateOutput=" + templateOutput +
                 '}';
     }
diff --git a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/CompositeGroupDemo.java b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/CompositeGroupDemo.java
new file mode 100644
index 0000000..45bad76
--- /dev/null
+++ b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/CompositeGroupDemo.java
@@ -0,0 +1,77 @@
+package org.apache.freemarker.generator.cli.config;
+
+import org.apache.commons.lang3.Validate;
+import picocli.CommandLine;
+import picocli.CommandLine.ArgGroup;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.util.List;
+
+@Command(name = "repeating-composite-demo")
+public class CompositeGroupDemo {
+
+    @ArgGroup(exclusive = false, multiplicity = "1..*")
+    List<OutputGenerator> outputGenerators;
+
+    static class OutputDefinition {
+        @Option(names = { "-o", "--output" }, description = "output files or directories") List<String> outputs;
+    }
+
+    static class DataSourceDefinition {
+        @Option(names = { "-s", "--data-source" }, description = "data source used for rendering") List<String> dataSources;
+    }
+
+    static class TemplateDefinition {
+        @Option(names = { "-t", "--template" }, description = "templates to process") String template;
+        @Option(names = { "-i", "--interactive" }, description = "interactive template to process") public String interactiveTemplate;
+    }
+
+    static class OutputGenerator {
+        @ArgGroup(multiplicity = "1")
+        TemplateDefinition templateDefinition;
+
+        @ArgGroup(exclusive = false)
+        DataSourceDefinition dataSourceDefinition;
+
+        @ArgGroup(exclusive = false)
+        OutputDefinition outputDefinition;
+    }
+
+    public static void main(String[] args) {
+        final CompositeGroupDemo compositeGroupDemo = new CompositeGroupDemo();
+        final CommandLine cmd = new CommandLine(compositeGroupDemo);
+
+        cmd.parseArgs(
+                "-t", "template01.ftl", "-s", "datasource10.csv",
+                "-t", "template02.ftl", "-s", "datasource20.csv", "-s", "datasource21.csv",
+                "-i", "some-interactive-template01", "-s", "datasource30.csv", "-o", "out.txt",
+                "-i", "some-interactive-template02"
+        );
+
+        final List<OutputGenerator> outputGenerators = compositeGroupDemo.outputGenerators;
+
+        Validate.notNull(outputGenerators);
+        Validate.isTrue(outputGenerators.size() == 4);
+
+        Validate.isTrue(outputGenerators.get(0).templateDefinition.template.equals("template01.ftl"));
+        Validate.isTrue(outputGenerators.get(0).dataSourceDefinition.dataSources.size() == 1);
+        Validate.isTrue(outputGenerators.get(0).dataSourceDefinition.dataSources.get(0).equals("datasource10.csv"));
+        Validate.isTrue(outputGenerators.get(0).outputDefinition == null);
+
+        Validate.isTrue(outputGenerators.get(1).templateDefinition.template.equals("template02.ftl"));
+        Validate.isTrue(outputGenerators.get(1).dataSourceDefinition.dataSources.size() == 2);
+        Validate.isTrue(outputGenerators.get(1).dataSourceDefinition.dataSources.get(0).equals("datasource20.csv"));
+        Validate.isTrue(outputGenerators.get(1).dataSourceDefinition.dataSources.get(1).equals("datasource21.csv"));
+        Validate.isTrue(outputGenerators.get(0).outputDefinition == null);
+
+        Validate.isTrue(outputGenerators.get(2).templateDefinition.interactiveTemplate.equals("some-interactive-template01"));
+        Validate.isTrue(outputGenerators.get(2).dataSourceDefinition.dataSources.size() == 1);
+        Validate.isTrue(outputGenerators.get(2).dataSourceDefinition.dataSources.get(0).equals("datasource30.csv"));
+        Validate.isTrue(outputGenerators.get(2).outputDefinition.outputs.get(0).equals("out.txt"));
+
+        Validate.isTrue(outputGenerators.get(3).templateDefinition.interactiveTemplate.equals("some-interactive-template02"));
+
+        return;
+    }
+}
\ No newline at end of file


[freemarker-generator] 03/06: FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI

Posted by sg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sgoeschl pushed a commit to branch FREEMARKER-161
in repository https://gitbox.apache.org/repos/asf/freemarker-generator.git

commit 045dc66bace34fe3f592373749d8b3b04b82cc84
Author: Siegfried Goeschl <si...@gmail.com>
AuthorDate: Thu Dec 24 16:37:03 2020 +0100

    FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI
---
 .../base/template/TemplateTransformation.java         | 19 +------------------
 1 file changed, 1 insertion(+), 18 deletions(-)

diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformation.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformation.java
index 1d0ce7d..6f0ed90 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformation.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformation.java
@@ -16,8 +16,6 @@
  */
 package org.apache.freemarker.generator.base.template;
 
-import org.apache.freemarker.generator.base.datasource.DataSources;
-
 import static java.util.Objects.requireNonNull;
 
 /**
@@ -28,21 +26,11 @@ public class TemplateTransformation {
     /** Source of template */
     private final TemplateSource templateSource;
 
-    /** Data sources being transformed */
-    private final DataSources dataSources;
-
     /** Output of template */
     private final TemplateOutput templateOutput;
 
     public TemplateTransformation(TemplateSource templateSource, TemplateOutput templateOutput) {
         this.templateSource = requireNonNull(templateSource);
-        this.dataSources = null;
-        this.templateOutput = requireNonNull(templateOutput);
-    }
-
-    public TemplateTransformation(TemplateSource templateSource, DataSources dataSources, TemplateOutput templateOutput) {
-        this.templateSource = requireNonNull(templateSource);
-        this.dataSources = requireNonNull(dataSources);
         this.templateOutput = requireNonNull(templateOutput);
     }
 
@@ -54,15 +42,10 @@ public class TemplateTransformation {
         return templateOutput;
     }
 
-    public DataSources getDataSources() {
-        return dataSources;
-    }
-
     @Override
     public String toString() {
-        return "TemplateTransformation{" +
+        return "OutputGenerator{" +
                 "templateSource=" + templateSource +
-                "dataSources=" + dataSources +
                 ", templateOutput=" + templateOutput +
                 '}';
     }