FREEMARKER-161 [freemarker-generator] Allow multiple transformations on the CLI (#25)
diff --git a/NOTICE b/NOTICE
index c27f50e..60b453f 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
Apache FreeMarker Site
-Copyright 2020 The Apache Software Foundation
+Copyright 2021 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/file/RecursiveFileSupplier.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/file/RecursiveFileSupplier.java
index b2498d8..bc89df3 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/file/RecursiveFileSupplier.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/file/RecursiveFileSupplier.java
@@ -85,7 +85,7 @@
} else if (file.isDirectory()) {
return resolveDirectory(file);
} else {
- throw new IllegalArgumentException("Unable to find source: " + source);
+ throw new IllegalArgumentException("Unable to find file: " + source);
}
}
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..5ce4cf4
--- /dev/null
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/output/OutputGenerator.java
@@ -0,0 +1,81 @@
+/*
+ * 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;
+
+ /** Variables (as a map) 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 +
+ ", dataSources=" + dataSources +
+ ", variables=" + variables +
+ '}';
+ }
+}
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateOutput.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateOutput.java
index 818d66d..89a8bb5 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateOutput.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateOutput.java
@@ -16,12 +16,9 @@
*/
package org.apache.freemarker.generator.base.template;
-import org.apache.freemarker.generator.base.util.Validate;
-
import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
import java.io.Writer;
+import java.nio.charset.Charset;
import static java.util.Objects.requireNonNull;
@@ -36,43 +33,42 @@
private final Writer writer;
private final File file;
+ private final Charset charset;
- private TemplateOutput(File file) {
+ private TemplateOutput(File file, final Charset charset) {
this.writer = null;
this.file = requireNonNull(file);
+ this.charset = requireNonNull(charset);
}
private TemplateOutput(Writer writer) {
this.writer = requireNonNull(writer);
this.file = null;
+ this.charset = null;
}
public static TemplateOutput fromWriter(Writer writer) {
return new TemplateOutput(writer);
}
- public static TemplateOutput fromFile(File file) {
- return new TemplateOutput(file);
+ public static TemplateOutput fromFile(File file, final Charset charset) {
+ return new TemplateOutput(file, charset);
}
public Writer getWriter() {
return writer;
}
+ public boolean hasWriter() {
+ return writer != null;
+ }
+
public File getFile() {
return file;
}
- public boolean isWrittenToFile() {
- return file != null;
- }
-
- public boolean isWrittenToSuppliedWriter() {
- return writer != null;
- }
-
- public Writer writer() {
- return writer != null ? writer : fileWriter();
+ public Charset getCharset() {
+ return charset;
}
@Override
@@ -80,16 +76,7 @@
return "TemplateOutput{" +
"writer=" + writer +
", file=" + file +
+ ", charset=" + charset +
'}';
}
-
- private FileWriter fileWriter() {
- Validate.notNull(file, "Output file is null");
-
- try {
- return new FileWriter(file);
- } catch (IOException e) {
- throw new RuntimeException("Failed to create FileWriter: " + file.getAbsolutePath(), e);
- }
- }
}
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..a8b0c65 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 @@
public class TemplateSource {
public enum Origin {
- PATH,
- CODE
+ TEMPLATE_LOADER,
+ TEMPLATE_CODE
}
/** Name of template for diagnostics */
@@ -50,7 +50,7 @@
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 @@
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;
@@ -67,22 +67,10 @@
/**
* Template will be loaded from path using a file-base template loader.
*
- * @param path template path
- * @return file-based template source
- */
- public static TemplateSource fromPath(String path) {
- Validate.notEmpty(path, "Template path is empty");
- return new TemplateSource(path, path, StandardCharsets.UTF_8);
- }
-
- /**
- * Template will be loaded from path using a file-base template loader.
- *
* @param path template path
* @param encoding character encoding og template
* @return file-based template source
*/
-
public static TemplateSource fromPath(String path, Charset encoding) {
Validate.notEmpty(path, "Template path is empty");
Validate.notNull(encoding, "Template encoding is null");
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/TemplateTransformation.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformation.java
index 8c19478..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
@@ -44,7 +44,7 @@
@Override
public String toString() {
- return "TemplateTransformation{" +
+ return "OutputGenerator{" +
"templateSource=" + templateSource +
", templateOutput=" + templateOutput +
'}';
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..6aaf494 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.NonClosableWriterWrapper;
import org.apache.freemarker.generator.base.util.StringUtils;
@@ -26,24 +28,24 @@
import java.io.File;
import java.io.OutputStreamWriter;
import java.io.Writer;
+import java.nio.charset.Charset;
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;
+ /** Template source - either a single template or a directory */
+ private String templateSource;
/** Optional include patterns for resolving source templates or template directories */
private final List<String> includes;
@@ -51,59 +53,56 @@
/** Optional exclude patterns for resolving source templates or template directories */
private final List<String> excludes;
- /** Optional output file or directory */
- private final List<String> outputs;
+ /** Encoding used to load templates */
+ private Charset templateEncoding;
- /** Optional user-supplied writer */
- private Writer writer;
+ /** Optional output file or directory - if none is defined everything is written to STDOUT */
+ private String output;
+
+ /** Optional output encoding */
+ private Charset outputEncoding;
+
+ /** Optional caller-supplied writer used for testing */
+ private Writer callerSuppliedWriter;
private TemplateTransformationsBuilder() {
- this.sources = new ArrayList<>();
+ this.templateSource = null;
this.includes = new ArrayList<>();
this.excludes = new ArrayList<>();
- this.outputs = new ArrayList<>();
- this.writer = null;
+ this.templateEncoding = UTF_8;
+ this.output = null;
+ this.callerSuppliedWriter = null;
+ this.outputEncoding = UTF_8;
}
public static TemplateTransformationsBuilder builder() {
return new TemplateTransformationsBuilder();
}
- public TemplateTransformations build() {
+ public List<TemplateTransformation> build() {
validate();
final List<TemplateTransformation> result = new ArrayList<>();
+ final File outputFile = getOutputFile();
if (hasInteractiveTemplate()) {
- 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);
- final File output = getOutputFile(i).orElse(null);
- result.addAll(resolve(source, output));
- }
+ result.addAll(resolve(templateSource, outputFile));
}
- 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) {
- if (StringUtils.isNotEmpty(source)) {
- this.sources.add(source);
- }
- return this;
- }
-
- public TemplateTransformationsBuilder addSources(Collection<String> sources) {
- sources.forEach(this::addSource);
+ public TemplateTransformationsBuilder setTemplateSource(String source) {
+ this.templateSource = source;
return this;
}
@@ -114,13 +113,6 @@
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,40 +120,34 @@
return this;
}
- public TemplateTransformationsBuilder addExcludes(Collection<String> excludes) {
- if (excludes != null) {
- this.excludes.addAll(excludes);
+ public TemplateTransformationsBuilder setTemplateEncoding(Charset charset) {
+ if (charset != null) {
+ this.templateEncoding = charset;
}
return this;
}
- public TemplateTransformationsBuilder addOutputs(Collection<String> outputs) {
- if (outputs != null) {
- this.outputs.addAll(outputs);
+ public TemplateTransformationsBuilder setOutput(String output) {
+ this.output = output;
+ return this;
+ }
+
+ public TemplateTransformationsBuilder setOutputEncoding(Charset outputEncoding) {
+ if (outputEncoding != null) {
+ // keep UTF-8 here
+ this.outputEncoding = outputEncoding;
}
+
return this;
}
- public TemplateTransformationsBuilder addOutput(String output) {
- if (StringUtils.isNotEmpty(output)) {
- this.outputs.add(output);
- }
- return this;
- }
-
- public TemplateTransformationsBuilder setWriter(Writer writer) {
- this.writer = writer;
- return this;
- }
-
- public TemplateTransformationsBuilder setStdOut() {
- this.writer = new NonClosableWriterWrapper(new BufferedWriter(new OutputStreamWriter(System.out, UTF_8)));
+ public TemplateTransformationsBuilder setCallerSuppliedWriter(Writer callerSuppliedWriter) {
+ this.callerSuppliedWriter = callerSuppliedWriter;
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 || templateSource == null, "No template was provided");
}
/**
@@ -172,14 +158,16 @@
* @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);
+ } 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);
}
}
@@ -206,33 +194,43 @@
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 List<TemplateTransformation> resolveTemplateUri(String source, File out) {
+ final TemplateSource templateSource = templateSource(source);
+ final TemplateOutput templateOutput = templateOutput(out);
+ return singletonList(new TemplateTransformation(templateSource, templateOutput));
+ }
+
+ private List<TemplateTransformation> resolveTemplatePath(String source, File out) {
+ final TemplateSource templateSource = TemplateSource.fromPath(source, templateEncoding);
+ final TemplateOutput templateOutput = templateOutput(out);
+ return singletonList(new TemplateTransformation(templateSource, templateOutput));
+ }
+
private TemplateTransformation resolveInteractiveTemplate(File out) {
final TemplateOutput templateOutput = templateOutput(out);
- return new TemplateTransformation(template, templateOutput);
- }
-
- private List<TemplateTransformation> resolveTemplateCode(String source, File out) {
- final TemplateSource templateSource = TemplateSource.fromCode(Location.INTERACTIVE, source);
- final TemplateOutput templateOutput = templateOutput(out);
- return singletonList(new TemplateTransformation(templateSource, templateOutput));
+ return new TemplateTransformation(interactiveTemplate, templateOutput);
}
private TemplateOutput templateOutput(File templateOutputFile) {
- if (writer == null && templateOutputFile != null) {
- return TemplateOutput.fromFile(templateOutputFile);
+ if (callerSuppliedWriter != null) {
+ return TemplateOutput.fromWriter(callerSuppliedWriter);
+ } else if (templateOutputFile != null) {
+ return TemplateOutput.fromFile(templateOutputFile, outputEncoding);
} else {
- return TemplateOutput.fromWriter(writer);
+ return TemplateOutput.fromWriter(stdoutWriter(outputEncoding));
}
}
private TemplateSource templateSource(String source) {
- return TemplateSourceFactory.create(source);
+ try (DataSource dataSource = DataSourceFactory.create(source)) {
+ return TemplateSource.fromCode(dataSource.getName(), dataSource.getText(templateEncoding.name()));
+ }
}
private String getInclude() {
@@ -244,37 +242,40 @@
}
private boolean hasInteractiveTemplate() {
- return template != null;
+ return interactiveTemplate != null;
}
- private Optional<File> getOutputFile(int i) {
- if (outputs.isEmpty()) {
- return Optional.empty();
- } else if (i < outputs.size()) {
- return Optional.of(new File(outputs.get(i)));
- } else {
- return Optional.of(new File(outputs.get(0)));
- }
+ private File getOutputFile() {
+ return output == null ? null : new File(output);
}
private static File getTemplateOutputFile(File templateDirectory, File templateFile, File outputDirectory) {
+ if (outputDirectory == null) {
+ // missing output directory uses STDOUT
+ return null;
+ }
+
final String relativePath = relativePath(templateDirectory, templateFile);
final String relativeOutputFileName = mapExtension(relativePath);
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);
+ 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) {
@@ -294,4 +295,9 @@
return fileName;
}
}
+
+ private Writer stdoutWriter(Charset outputEncoding) {
+ // avoid closing System.out after rendering the template
+ return new BufferedWriter(new NonClosableWriterWrapper(new OutputStreamWriter(System.out, outputEncoding)));
+ }
}
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..561c13b 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
@@ -22,18 +22,18 @@
public class MapBuilder {
- public static Map<String, Object> toLinkedMap(Object... data) {
+ public static Map<String, Object> toLinkedMap(Object... nameValuePairs) {
final HashMap<String, Object> map = new LinkedHashMap<>();
- if (data.length % 2 != 0) {
+ if (nameValuePairs.length % 2 != 0) {
throw new IllegalArgumentException("Odd number of arguments");
}
String currKey = null;
int step = -1;
- for (Object value : data) {
+ for (Object value : nameValuePairs) {
step++;
switch (step % 2) {
case 0:
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapFlattener.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapFlattener.java
deleted file mode 100644
index c1ad75a..0000000
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/MapFlattener.java
+++ /dev/null
@@ -1,140 +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.util;
-
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.function.Function;
-import java.util.function.UnaryOperator;
-
-/**
- * Flattens a hierarchical {@link Map} of objects into a property {@link Map}.
- * <p>
- * Flattening is particularly useful when representing a JSON object as
- * {@link java.util.Properties}
- * <p>
- * {@link MapFlattener} flattens {@link Map maps} containing nested
- * {@link java.util.List}, {@link Map} and simple values into a flat representation. The
- * hierarchical structure is reflected in properties using dot-notation. Nested maps are
- * considered as sub-documents.
- * <p>
- * Input:
- *
- * <pre class="code">
- * {"key": {"nested: 1}, "another.key": ["one", "two"] }
- * </pre>
- *
- * <br>
- * Result
- *
- * <pre class="code">
- * key.nested=1
- * another.key[0]=one
- * another.key[1]=two
- * </pre>
- *
- * @author Mark Paluch
- * <p>
- * Copied from https://github.com/spring-projects/spring-vault/blob/master/spring-vault-core/src/main/java/org/springframework/vault/support/JsonMapFlattener.java
- */
-public abstract class MapFlattener {
-
- private MapFlattener() {
- }
-
- /**
- * Flatten a hierarchical {@link Map} into a flat {@link Map} with key names using
- * property dot notation.
- *
- * @param inputMap must not be {@literal null}.
- * @return the resulting {@link Map}.
- */
- public static Map<String, Object> flatten(Map<String, ?> inputMap) {
- Validate.notNull(inputMap, "Input Map must not be null");
-
- final Map<String, Object> resultMap = new LinkedHashMap<>();
- doFlatten("", inputMap.entrySet().iterator(), resultMap, UnaryOperator.identity());
- return resultMap;
- }
-
- /**
- * Flatten a hierarchical {@link Map} into a flat {@link Map} with key names using
- * property dot notation.
- *
- * @param inputMap must not be {@literal null}.
- * @return the resulting {@link Map}.
- * @since 2.0
- */
- public static Map<String, String> flattenToStringMap(Map<String, ?> inputMap) {
- Validate.notNull(inputMap, "inputMap is null");
-
- final Map<String, String> resultMap = new LinkedHashMap<>();
- doFlatten("", inputMap.entrySet().iterator(), resultMap, it -> it == null ? null : it.toString());
- return resultMap;
- }
-
- private static void doFlatten(String propertyPrefix,
- Iterator<? extends Entry<String, ?>> inputMap,
- Map<String, ?> resultMap,
- Function<Object, Object> valueTransformer) {
-
- if (StringUtils.isNotEmpty(propertyPrefix)) {
- propertyPrefix = propertyPrefix + ".";
- }
-
- while (inputMap.hasNext()) {
- final Entry<String, ?> entry = inputMap.next();
- flattenElement(
- propertyPrefix.concat(entry.getKey()),
- entry.getValue(),
- resultMap,
- valueTransformer);
- }
- }
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- private static void flattenElement(String propertyPrefix, Object source,
- Map<String, ?> resultMap, Function<Object, Object> valueTransformer) {
-
- if (source instanceof Iterable) {
- flattenCollection(propertyPrefix, (Iterable<Object>) source, resultMap,
- valueTransformer);
- return;
- }
-
- if (source instanceof Map) {
- doFlatten(propertyPrefix, ((Map<String, ?>) source).entrySet().iterator(),
- resultMap, valueTransformer);
- return;
- }
-
- ((Map) resultMap).put(propertyPrefix, valueTransformer.apply(source));
- }
-
- private static void flattenCollection(String propertyPrefix,
- Iterable<Object> iterable, Map<String, ?> resultMap,
- Function<Object, Object> valueTransformer) {
-
- int counter = 0;
- for (Object element : iterable) {
- flattenElement(propertyPrefix + "[" + counter + "]", element, resultMap, valueTransformer);
- counter++;
- }
- }
-}
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/OperatingSystem.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/OperatingSystem.java
new file mode 100644
index 0000000..2a4c84c
--- /dev/null
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/OperatingSystem.java
@@ -0,0 +1,40 @@
+/*
+ * 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.util;
+
+import java.util.Locale;
+
+/**
+ * Helper class to detect the operting system (mostly Windows).
+ *
+ * TODO should be moved to "freemarker-generator-base"
+ */
+public class OperatingSystem {
+ private static final String OS = System.getProperty("os.name", "unknown").toLowerCase(Locale.ROOT);
+
+ public static boolean isWindows() {
+ return OS.contains("win");
+ }
+
+ public static boolean isMac() {
+ return OS.contains("mac");
+ }
+
+ public static boolean isUnix() {
+ return OS.contains("nux");
+ }
+}
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/UriUtils.java b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/UriUtils.java
index fab27e0..b8135bb 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/UriUtils.java
+++ b/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/util/UriUtils.java
@@ -50,7 +50,7 @@
public static String toStringWithoutFragment(URI uri) {
final String str = uri.toString();
final int index = str.indexOf('#');
- return (index > 0) ? str.substring(0, index) : str;
+ return index > 0 ? str.substring(0, index) : str;
}
public static boolean isUri(String str) {
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 0124a98..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.PATH, 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.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.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.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.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..822b66a 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,16 +20,23 @@
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.File;
+import java.io.BufferedWriter;
+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.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
public class TemplateTransformationsBuilderTest {
@@ -37,14 +44,16 @@
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 =============================================
@Test
public void shouldCreateFromInteractiveTemplate() {
- final TemplateTransformations transformations = builder()
- .setTemplate(Location.INTERACTIVE, "Hello World")
- .setStdOut()
+ final List<TemplateTransformation> transformations = builder()
+ .setInteractiveTemplate("Hello World")
+ .setCallerSuppliedWriter(stdoutWriter())
.build();
assertEquals(1, transformations.size());
@@ -53,7 +62,7 @@
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 +74,19 @@
@Test(expected = IllegalArgumentException.class)
public void shouldThrowIllegalArgumentExceptionWheMixingInteractiveTemplateWithSources() {
builder()
- .setTemplate(Location.INTERACTIVE, "Hello World")
- .addSource(ANY_TEMPLATE_FILE_NAME)
- .setStdOut()
+ .setInteractiveTemplate("Hello World")
+ .setTemplateSource(ANY_TEMPLATE_FILE_NAME)
+ .setCallerSuppliedWriter(stdoutWriter())
.build();
}
-
// === Template File ====================================================
@Test
public void shouldCreateFromTemplateFile() {
- final TemplateTransformations transformations = builder()
- .addSource(ANY_TEMPLATE_FILE_NAME)
- .setStdOut()
+ final List<TemplateTransformation> transformations = builder()
+ .setTemplateSource(ANY_TEMPLATE_FILE_NAME)
+ .setCallerSuppliedWriter(stdoutWriter())
.build();
assertEquals(1, transformations.size());
@@ -87,8 +95,8 @@
final TemplateOutput templateOutput = transformations.get(0).getTemplateOutput();
assertNotNull(templateSource.getName());
- assertEquals(Origin.CODE, templateSource.getOrigin());
- assertNotNull(templateSource.getCode());
+ assertEquals(Origin.TEMPLATE_CODE, templateSource.getOrigin());
+ assertTrue(templateSource.getCode().contains("Licensed to the Apache Software Foundation"));
assertNull(templateSource.getPath());
assertEquals(StandardCharsets.UTF_8, templateSource.getEncoding());
@@ -96,27 +104,13 @@
assertNull(templateOutput.getFile());
}
- @Test
- public void shouldCreateFromMultipleTemplateFiles() {
- final TemplateTransformations transformations = builder()
- .addSource(ANY_TEMPLATE_FILE_NAME)
- .addOutput("foo/first.out")
- .addSource(OTHER_TEMPLATE_FILE_NAME)
- .addOutput("foo/second.out")
- .build();
-
- assertEquals(2, transformations.size());
- assertEquals(new File("foo/first.out"), transformations.get(0).getTemplateOutput().getFile());
- assertEquals(new File("foo/second.out"), transformations.get(1).getTemplateOutput().getFile());
- }
-
// === Template Path ====================================================
@Test
public void shouldCreateFromTemplatePath() {
- final TemplateTransformations transformations = builder()
- .addSource(ANY_TEMPLATE_PATH)
- .setStdOut()
+ final List<TemplateTransformation> transformations = builder()
+ .setTemplateSource(ANY_TEMPLATE_PATH)
+ .setCallerSuppliedWriter(stdoutWriter())
.build();
assertEquals(1, transformations.size());
@@ -124,10 +118,10 @@
final TemplateSource templateSource = transformations.get(0).getTemplateSource();
final TemplateOutput templateOutput = transformations.get(0).getTemplateOutput();
- assertNotNull(templateSource.getName());
- assertEquals(Origin.PATH, templateSource.getOrigin());
+ 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());
@@ -138,9 +132,9 @@
@Test
public void shouldCreateFromTemplateDirectory() {
- final TemplateTransformations transformations = builder()
- .addSource(ANY_TEMPLATE_DIRECTORY_NAME)
- .setStdOut()
+ final List<TemplateTransformation> transformations = builder()
+ .setTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
+ .setCallerSuppliedWriter(stdoutWriter())
.build();
assertEquals(2, transformations.size());
@@ -150,9 +144,9 @@
@Test
public void shouldCreateFromTemplateDirectoryWithOutputDirectory() {
- final TemplateTransformations transformations = builder()
- .addSource(ANY_TEMPLATE_DIRECTORY_NAME)
- .addOutput("/foo")
+ final List<TemplateTransformation> transformations = builder()
+ .setTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
+ .setOutput("/foo")
.build();
assertEquals(2, transformations.size());
@@ -162,10 +156,10 @@
@Test
public void shouldCreateFromTemplateDirectoryWithInclude() {
- final TemplateTransformations transformations = builder()
- .addSource(ANY_TEMPLATE_DIRECTORY_NAME)
+ final List<TemplateTransformation> transformations = builder()
+ .setTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
.addInclude("*.properties")
- .setStdOut()
+ .setCallerSuppliedWriter(stdoutWriter())
.build();
assertEquals(1, transformations.size());
@@ -174,18 +168,60 @@
@Test
public void shouldCreateFromTemplateDirectoryWithExclude() {
- final TemplateTransformations transformations = builder()
- .addSource(ANY_TEMPLATE_DIRECTORY_NAME)
+ final List<TemplateTransformation> transformations = builder()
+ .setTemplateSource(ANY_TEMPLATE_DIRECTORY_NAME)
.addExclude("*.ftl")
- .setStdOut()
+ .setCallerSuppliedWriter(stdoutWriter())
.build();
assertEquals(1, transformations.size());
assertEquals("application.properties", transformations.get(0).getTemplateSource().getName());
}
+ // === Template URL ===============================================
+
+ @Test
+ public void shouldCreateFromTemplateUrl() {
+ final List<TemplateTransformation> transformations = builder()
+ .setTemplateSource(ANY_TEMPLATE_URL)
+ .setCallerSuppliedWriter(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()
+ .setTemplateSource(ANY_ENV_URI)
+ .setCallerSuppliedWriter(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();
}
+
+ private Writer stdoutWriter() {
+ return new NonClosableWriterWrapper(new BufferedWriter(new OutputStreamWriter(System.out, UTF_8)));
+ }
}
diff --git a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/util/MapFlattenerTest.java b/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/util/MapFlattenerTest.java
deleted file mode 100644
index 92bd90c..0000000
--- a/freemarker-generator-base/src/test/java/org/apache/freemarker/generator/util/MapFlattenerTest.java
+++ /dev/null
@@ -1,43 +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.util;
-
-import org.apache.freemarker.generator.base.util.MapFlattener;
-import org.junit.Test;
-
-import java.util.Collections;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class MapFlattenerTest {
-
- @Test
- public void shouldHandleEmptyMap() {
- final Map<String, Object> result = MapFlattener.flatten(Collections.emptyMap());
-
- assertTrue(result.isEmpty());
- }
-
- @Test
- public void shouldPreserveFlatMap() {
- final Map<String, Object> result = MapFlattener.flatten(Collections.singletonMap("key", "value"));
-
- assertEquals("value", result.get("key"));
- }
-}
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 @@
* [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 @@
[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/LICENSE b/freemarker-generator-cli/LICENSE
index caca01c..f6a36bd 100644
--- a/freemarker-generator-cli/LICENSE
+++ b/freemarker-generator-cli/LICENSE
@@ -335,6 +335,12 @@
==============================================================================
+Binary distributions of this product bundles java-faker which
+is available under Apache License Version 2.0.
+See licencens/LICENSE_ASL-2.0.txt for more information ...
+
+==============================================================================
+
Binary distributions of this product bundles poi which
is available under Apache License Version 2.0.
See licencens/LICENSE_ASL-2.0.txt for more information ...
diff --git a/freemarker-generator-cli/pom.xml b/freemarker-generator-cli/pom.xml
index da3494b..c21d5d9 100644
--- a/freemarker-generator-cli/pom.xml
+++ b/freemarker-generator-cli/pom.xml
@@ -161,10 +161,12 @@
<configuration>
<excludes>
<exclude>README.md</exclude>
- <exclude>src/site/markdown/**/*.md</exclude>
+ <exclude>CHANGELOG.md</exclude>
<exclude>.travis.yml</exclude>
+ <exclude>src/app/examples/data/*/**</exclude>
+ <exclude>src/app/scripts/run-examples.bat</exclude>
<exclude>src/main/resources/patterns/*</exclude>
- <exclude>examples/data/*/**</exclude>
+ <exclude>src/site/markdown/**/*.md</exclude>
<exclude>src/test/data/encoding/utf8.txt</exclude>
<exclude>src/test/data/json/*/**</exclude>
<exclude>src/test/data/yaml/environments.yaml</exclude>
@@ -179,7 +181,7 @@
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
- <version>4.1.4</version>
+ <version>4.6.1</version>
</dependency>
<!-- Apache FreeMarker Generator Tools -->
<dependency>
diff --git a/freemarker-generator-cli/src/app/examples/templates/demo.ftl b/freemarker-generator-cli/src/app/examples/templates/demo.ftl
index 8ec3ac4..879fb3f 100644
--- a/freemarker-generator-cli/src/app/examples/templates/demo.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/demo.ftl
@@ -127,7 +127,10 @@
15) Printing Special Characters
---------------------------------------------------------------------------
+Windows 1252 Characters: €¥§ÆÇæ®
German Special Characters: äöüßÄÖÜ
+Cyrillic Characters: Кириллица
+Chinese Characters: 你好吗
16) Locale-specific output
---------------------------------------------------------------------------
diff --git a/freemarker-generator-cli/src/app/examples/templates/javafaker/csv/testdata.ftl b/freemarker-generator-cli/src/app/examples/templates/javafaker/csv/testdata.ftl
index 357a3ae..1976644 100644
--- a/freemarker-generator-cli/src/app/examples/templates/javafaker/csv/testdata.ftl
+++ b/freemarker-generator-cli/src/app/examples/templates/javafaker/csv/testdata.ftl
@@ -14,8 +14,9 @@
specific language governing permissions and limitations
under the License.
-->
-<#assign faker = tools.javafaker.faker>
-<#assign nrOfRecords = tools.system.getString("NR_OF_RECORDS","10")>
+<#-- Get a localized JavaFaker instance -->
+<#assign faker = tools.javafaker.getFaker("de_DE")>
+<#assign nrOfRecords = tools.system.getString("NR_OF_RECORDS","100")>
<#assign days = tools.javafaker.timeUnits["DAYS"]>
<#assign csvTargetFormat = tools.csv.formats["DEFAULT"].withFirstRecordAsHeader()>
<#assign csvPrinter = tools.csv.printer(csvTargetFormat)>
@@ -24,15 +25,18 @@
<#if csvTargetFormat.getSkipHeaderRecord()>
${csvPrinter.printRecord(csvHeaders)}<#t>
</#if>
- <#list 1..nrOfRecords?number as i>
- <#assign id = tools.uuid.randomUUID()>
+ <#list 0..nrOfRecords?number as i>
+ <#-- Generate a reproducable id to allow re-importing of test data -->
+ <#assign id = tools.uuid.namedUUID("trxid-" + i?string)>
<#assign customerId = faker.bothify("?#######")>
<#assign firstName = faker.name().firstName()>
<#assign lastName = faker.name().lastName()>
- <#assign email = firstName + "." + lastName + "@gmail.com">
+ <#assign email = firstName + "." + lastName + "@server.invalid">
+ <#-- JavaFakers IBAN generation is really slow -->
<#assign iban = faker.finance().iban("DE")>
-
+ <#-- Distribute the creation date up to 10 years in the past -->
<#assign createAt = faker.date().past(3650, days)>
+ <#-- Use a CSV Printer to properly escape the output -->
${csvPrinter.printRecord(
id,
customerId,
diff --git a/freemarker-generator-cli/src/app/scripts/run-examples.bat b/freemarker-generator-cli/src/app/scripts/run-examples.bat
index 4bb5e5e..6ef6f9a 100644
--- a/freemarker-generator-cli/src/app/scripts/run-examples.bat
+++ b/freemarker-generator-cli/src/app/scripts/run-examples.bat
@@ -130,7 +130,7 @@
REM Java Faker
REM =========================================================================
echo "examples/templates/javafaker/csv/testdata.ftl"
-%FREEMARKER_CMD% -t examples/templates/javafaker/csv/testdata.ftl > target/out/testdata.csv
+%FREEMARKER_CMD% -DNR_OF_RECORDS=10 -t examples/templates/javafaker/csv/testdata.ftl > target/out/testdata.csv
REM =========================================================================
REM JSON
diff --git a/freemarker-generator-cli/src/app/scripts/run-examples.sh b/freemarker-generator-cli/src/app/scripts/run-examples.sh
index 316a098..034774f 100755
--- a/freemarker-generator-cli/src/app/scripts/run-examples.sh
+++ b/freemarker-generator-cli/src/app/scripts/run-examples.sh
@@ -151,7 +151,7 @@
#############################################################################
echo "examples/templates/javafaker/csv/testdata.ftl"
-$FREEMARKER_CMD -t examples/templates/javafaker/csv/testdata.ftl > target/out/testdata.csv || { echo >&2 "Test failed. Aborting."; exit 1; }
+$FREEMARKER_CMD -DNR_OF_RECORDS=10 -t examples/templates/javafaker/csv/testdata.ftl > target/out/testdata.csv || { echo >&2 "Test failed. Aborting."; exit 1; }
#############################################################################
# JSON
diff --git a/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl b/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl
index 1a34880..c165be3 100644
--- a/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl
+++ b/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl
@@ -37,24 +37,20 @@
- ${key}<#lt>
</#list>
-FreeMarker Generator Tools
-------------------------------------------------------------------------------
-<#list tools?keys?sort as name>
-- ${name?right_pad(20)} : ${tools[name]}
-</#list>
-
-<#if dataSources?has_content>
FreeMarker Generator DataSources
------------------------------------------------------------------------------
+<#if dataSources?has_content>
<#list dataSources?values as ds>
-[#${ds?counter}]: name=${ds.name}, group=${ds.group}, fileName=${ds.fileName} mimeType=${ds.mimeType}, charset=${ds.charset}, length=${ds.length} Bytes
+[#${ds?counter}]: name=${ds.name}, group=${ds.group}, fileName=${ds.fileName}, mimeType=${ds.mimeType}, charset=${ds.charset}, length=${ds.length} Bytes
URI : ${ds.uri}
</#list>
+<#else>
+No data sources found ...
</#if>
-<#if tools.system.parameters?has_content>
FreeMarker Generator Parameters
------------------------------------------------------------------------------
+<#if tools.system.parameters?has_content>
<#list tools.system.parameters as key,value>
<#if value?is_hash>
- ${key} ==> { <#list value as name,value>${name}=${value} </#list>}
@@ -62,4 +58,12 @@
- ${key} ==> ${value}
</#if>
</#list>
+<#else>
+No parameters found ...
</#if>
+
+FreeMarker Generator Tools
+------------------------------------------------------------------------------
+<#list tools?keys?sort as name>
+- ${name?right_pad(20)} : ${tools[name]}
+</#list>
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..5158668 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,53 +20,47 @@
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.cli.config.Settings;
+import org.apache.freemarker.generator.cli.config.Suppliers;
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;
-import java.io.BufferedWriter;
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.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.Collections.emptyList;
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.propertiesSupplier;
-import static org.apache.freemarker.generator.cli.config.Suppliers.templateDirectorySupplier;
@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 = { "--shared-data-model" }, description = "shared data models used for rendering")
+ public List<String> sharedDataModels;
@Option(names = { "-D", "--system-property" }, description = "set system property")
Properties systemProperties;
@@ -77,27 +71,12 @@
@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;
@@ -107,32 +86,32 @@
@Option(names = { "--template-dir" }, description = "additional template directory")
String templateDir;
+ @Option(names = { "--template-encoding" }, description = "template encoding", defaultValue = "UTF-8")
+ String templateEncoding;
+
@Option(names = { "--times" }, defaultValue = "1", description = "re-run X times for profiling")
int times;
- @Parameters(description = "data source files and/or directories")
- List<String> sources;
+ @Parameters(description = "shared data source files and/or directories")
+ List<String> sharedDataSources;
/** User-supplied command line parameters */
final String[] args;
/** User-supplied writer (used mainly for unit testing) */
- Writer userSuppliedWriter;
+ final Writer callerSuppliedWriter;
/** Injected by Picocli */
@Spec private CommandSpec spec;
- Main() {
- this.args = new String[0];
+ Main(String[] args) {
+ this.args = requireNonNull(args);
+ this.callerSuppliedWriter = null;
}
- private Main(String[] args) {
+ private Main(String[] args, Writer callerSuppliedWriter) {
this.args = requireNonNull(args);
- }
-
- private Main(String[] args, Writer userSuppliedWriter) {
- this.args = requireNonNull(args);
- this.userSuppliedWriter = requireNonNull(userSuppliedWriter);
+ this.callerSuppliedWriter = requireNonNull(callerSuppliedWriter);
}
public static void main(String[] args) {
@@ -171,77 +150,51 @@
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(
+ Suppliers.configurationSupplier(settings),
+ Suppliers.outputGeneratorsSupplier(settings),
+ Suppliers.sharedDataModelSupplier(settings),
+ Suppliers.sharedDataSourcesSupplier(settings),
+ 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)
+ .setTemplateEncoding(templateEncoding)
+ .setOutputGeneratorDefinitions(outputGeneratorDefinitions)
+ .setSharedDataSources(getSharedDataSources())
+ .setSharedDataModels(sharedDataModels)
+ .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)
+ .setVerbose(false)
.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,15 +207,12 @@
*
* @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> getSharedDataSources() {
+ return sharedDataSources != null ? new ArrayList<>(sharedDataSources) : emptyList();
}
private static List<File> getTemplateDirectories(String additionalTemplateDir) {
- return templateDirectorySupplier(additionalTemplateDir).get();
+ return Suppliers.templateDirectorySupplier(additionalTemplateDir).get();
}
/**
@@ -281,21 +231,11 @@
return new Properties();
}
- final Properties properties = propertiesSupplier(fileName).get();
+ final Properties properties = Suppliers.propertiesSupplier(fileName).get();
if (properties != null) {
return properties;
} else {
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/ConfigurationSupplier.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplier.java
index 8f45e90..60c7743 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplier.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplier.java
@@ -19,17 +19,16 @@
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Version;
+import org.apache.freemarker.generator.base.util.PropertiesTransformer;
import org.apache.freemarker.generator.cli.model.GeneratorObjectWrapper;
import java.util.Properties;
import java.util.function.Supplier;
+import java.util.stream.Stream;
import static freemarker.template.Configuration.VERSION_2_3_29;
import static java.util.Objects.requireNonNull;
-import static java.util.stream.Stream.of;
import static org.apache.freemarker.generator.base.FreeMarkerConstants.Configuration.SETTING_PREFIX;
-import static org.apache.freemarker.generator.base.util.PropertiesTransformer.filterKeyPrefix;
-import static org.apache.freemarker.generator.base.util.PropertiesTransformer.removeKeyPrefix;
/**
* Supply a FreeMarker configuration.
@@ -40,10 +39,12 @@
private final Settings settings;
private final Supplier<TemplateLoader> templateLoader;
+ private final ToolsSupplier toolsSupplier;
- public ConfigurationSupplier(Settings settings, Supplier<TemplateLoader> templateLoader) {
+ public ConfigurationSupplier(Settings settings, Supplier<TemplateLoader> templateLoader, ToolsSupplier toolsSupplier) {
this.settings = requireNonNull(settings);
this.templateLoader = requireNonNull(templateLoader);
+ this.toolsSupplier = requireNonNull(toolsSupplier);
}
@Override
@@ -63,6 +64,9 @@
configuration.setOutputEncoding(settings.getOutputEncoding().name());
configuration.setTemplateLoader(templateLoader.get());
+ // instantiate the tools as shared FreeMarker variables
+ configuration.setSharedVariables(toolsSupplier.get());
+
return configuration;
} catch (Exception e) {
throw new RuntimeException("Failed to create FreeMarker configuration", e);
@@ -76,9 +80,9 @@
* @return FreeMarker configuration settings
*/
private Properties freeMarkerConfigurationSettings() {
- return of(settings.getConfiguration())
- .map(p -> filterKeyPrefix(p, SETTING_PREFIX))
- .map(p -> removeKeyPrefix(p, SETTING_PREFIX))
+ return Stream.of(settings.getConfiguration())
+ .map(p -> PropertiesTransformer.filterKeyPrefix(p, SETTING_PREFIX))
+ .map(p -> PropertiesTransformer.removeKeyPrefix(p, SETTING_PREFIX))
.findFirst().get();
}
}
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..085ad60
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/OutputGeneratorsSupplier.java
@@ -0,0 +1,141 @@
+/*
+ * 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.config;
+
+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.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.base.util.UriUtils;
+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.net.URI;
+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;
+
+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.mime.Mimetypes.MIME_TEXT_PLAIN;
+
+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 = requireNonNull(definition.getTemplateSourceDefinition());
+ final TemplateOutputDefinition templateOutputDefinition = definition.getTemplateOutputDefinition();
+ final TemplateTransformationsBuilder builder = TemplateTransformationsBuilder.builder();
+
+ // set the template
+ if (templateSourceDefinition.isInteractiveTemplate()) {
+ builder.setInteractiveTemplate(templateSourceDefinition.interactiveTemplate);
+ } else {
+ builder.setTemplateSource(templateSourceDefinition.template);
+ }
+
+ // set encoding of loaded templates
+ builder.setTemplateEncoding(settings.getTemplateEncoding());
+
+ // set the writer
+ builder.setCallerSuppliedWriter(settings.getCallerSuppliedWriter());
+
+ // set template output
+ if (templateOutputDefinition != null && templateOutputDefinition.hasOutput()) {
+ builder.setOutput(templateOutputDefinition.outputs.get(0));
+ }
+
+ // set the output encoding
+ builder.setOutputEncoding(settings.getOutputEncoding());
+
+ // 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(settings, definition),
+ dataModels(definition)
+ );
+ result.add(outputGenerator);
+ }
+
+ return result;
+ }
+
+ private List<DataSource> dataSources(Settings settings, OutputGeneratorDefinition outputGeneratorDefinition) {
+ final ArrayList<DataSource> result = new ArrayList<>();
+
+ // Add optional data source from STDIN at the start of the list since
+ // this allows easy sequence slicing in FreeMarker.
+ if (settings.isReadFromStdin()) {
+ result.add(0, stdinDataSource());
+ }
+
+ 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();
+ }
+
+ private static DataSource stdinDataSource() {
+ final URI uri = UriUtils.toUri(Location.SYSTEM, STDIN);
+ return DataSourceFactory.fromInputStream(STDIN, DEFAULT_GROUP, uri, System.in, MIME_TEXT_PLAIN, UTF_8);
+ }
+}
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..c45a479 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 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,28 @@
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;
+ /** Encoding to load templates */
+ private final Charset templateEncoding;
- /** Template provided by the user interactively */
- private final String interactiveTemplate;
+ /** User-provided output generators (transformations) */
+ private final List<OutputGeneratorDefinition> outputGeneratorDefinitions;
- /** Optional include pattern for recursive directly search of template files */
- private final String templateFileIncludePattern;
+ /** List of additional shared data sources provided by positional parameters */
+ private final List<String> sharedDataSources;
- /** Optional exclude pattern for recursive directly search of data source files */
- private final String templateFileExcludePattern;
+ /** List of additional shared data models */
+ private final List<String> sharedDataModels;
+
+ /** Include pattern for sources */
+ private final String sourceIncludePattern;
+
+ /** Exclude pattern for sources */
+ private final String sourceExcludePattern;
/** Encoding of input files */
private final Charset inputEncoding;
@@ -75,81 +80,57 @@
/** 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,
+ Charset templateEncoding,
+ List<OutputGeneratorDefinition> outputGeneratorDefinitions,
+ List<String> sharedDataSources,
+ List<String> sharedDataModels,
+ 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.templateEncoding = requireNonNull(templateEncoding);
+ this.outputGeneratorDefinitions = requireNonNull(outputGeneratorDefinitions);
+ this.sharedDataSources = requireNonNull(sharedDataSources);
+ this.sharedDataModels = requireNonNull(sharedDataModels);
+ 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 +141,32 @@
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> getSharedDataSources() {
+ return sharedDataSources;
}
- public String getTemplateFileIncludePattern() {
- return templateFileIncludePattern;
+ public List<String> getSharedDataModels() {
+ return sharedDataModels;
}
- public String getTemplateFileExcludePattern() {
- return templateFileExcludePattern;
+ public String getSourceIncludePattern() {
+ return sourceIncludePattern;
+ }
+
+ public String getSourceExcludePattern() {
+ return sourceExcludePattern;
}
public Charset getInputEncoding() {
@@ -193,25 +178,13 @@
}
public Charset getTemplateEncoding() {
- return UTF_8;
+ return templateEncoding;
}
public boolean isVerbose() {
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 +193,6 @@
return isReadFromStdin;
}
- public List<String> getDataSources() {
- return dataSources;
- }
-
- public List<String> getDataModels() {
- return dataModels;
- }
-
public Map<String, Object> getUserParameters() {
return userParameters;
}
@@ -236,12 +201,8 @@
return userSystemProperties;
}
- public boolean hasOutputs() {
- return ListUtils.isNotEmpty(outputs);
- }
-
- public Writer getWriter() {
- return writer;
+ public Writer getCallerSuppliedWriter() {
+ return callerSuppliedWriter;
}
/**
@@ -252,113 +213,114 @@
*/
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 +
+ ", sharedDataSources=" + sharedDataSources +
+ ", sharedDataModels=" + sharedDataModels +
+ ", sourceIncludePattern='" + sourceIncludePattern + '\'' +
+ ", sourceExcludePattern='" + sourceExcludePattern + '\'' +
", inputEncoding=" + inputEncoding +
", outputEncoding=" + outputEncoding +
", verbose=" + verbose +
- ", outputs=" + outputs +
- ", include='" + dataSourceIncludePattern + '\'' +
- ", exclude='" + dataSourceExcludePattern + '\'' +
", locale=" + locale +
", isReadFromStdin=" + isReadFromStdin +
- ", dataSources=" + dataSources +
", userParameters=" + userParameters +
", userSystemProperties=" + userSystemProperties +
+ ", callerSuppliedWriter=" + callerSuppliedWriter +
+ ", templateEncoding=" + templateEncoding +
+ ", readFromStdin=" + isReadFromStdin() +
'}';
}
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 String templateEncoding;
+ private List<OutputGeneratorDefinition> outputGeneratorDefinitions;
+ private List<String> sharedDataSources;
+ private List<String> sharedDataModels;
+ 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.templateEncoding = UTF_8.name();
+ this.outputGeneratorDefinitions = emptyList();
+ this.sharedDataSources = emptyList();
+ this.sharedDataModels = 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();
- } else {
- this.args = Arrays.asList(args);
- }
-
+ public SettingsBuilder setCommandLineArgs(String[] args) {
+ this.commandLineArgs = args != null ? Arrays.asList(args) : emptyList();
return this;
}
public SettingsBuilder setTemplateDirectories(List<File> list) {
- this.templateDirectories = list;
+ this.templateDirectories = list != null ? new ArrayList<>(list) : emptyList();
return this;
}
- public SettingsBuilder setTemplateNames(List<String> templateNames) {
- if (templateNames != null) {
- this.templateNames = templateNames;
+ public SettingsBuilder setTemplateEncoding(String templateEncoding) {
+ if (templateEncoding != null) {
+ this.templateEncoding = templateEncoding;
}
return this;
}
- public SettingsBuilder setInteractiveTemplate(String interactiveTemplate) {
- this.interactiveTemplate = interactiveTemplate;
+ public SettingsBuilder setOutputGeneratorDefinitions(List<OutputGeneratorDefinition> outputGeneratorDefinitions) {
+ this.outputGeneratorDefinitions = outputGeneratorDefinitions != null ? new ArrayList<>(outputGeneratorDefinitions) : emptyList();
return this;
}
- public SettingsBuilder setTemplateFileIncludePattern(String templateFileIncludePattern) {
- this.templateFileIncludePattern = templateFileIncludePattern;
+ public SettingsBuilder setSharedDataSources(List<String> sharedDataSources) {
+ this.sharedDataSources = sharedDataSources != null ? new ArrayList<>(sharedDataSources) : emptyList();
return this;
}
- public SettingsBuilder setTemplateFileExcludePattern(String templateFileExcludePattern) {
- this.templateFileExcludePattern = templateFileExcludePattern;
+ public SettingsBuilder setSharedDataModels(List<String> sharedDataModels) {
+ this.sharedDataModels = sharedDataModels != null ? new ArrayList<>(sharedDataModels) : emptyList();
+ return this;
+ }
+
+ public SettingsBuilder setSourceIncludePattern(String sourceIncludePattern) {
+ this.sourceIncludePattern = sourceIncludePattern;
+ return this;
+ }
+
+ public SettingsBuilder setSourceExcludePattern(String sourceExcludePattern) {
+ this.sourceExcludePattern = sourceExcludePattern;
return this;
}
@@ -381,23 +343,6 @@
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 +353,6 @@
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,37 +374,32 @@
return this;
}
- public SettingsBuilder setWriter(Writer writer) {
- this.writer = writer;
+ public SettingsBuilder setCallerSuppliedWriter(Writer callerSuppliedWriter) {
+ this.callerSuppliedWriter = callerSuppliedWriter;
return this;
}
public Settings build() {
- final Charset inputEncoding = Charset.forName(this.inputEncoding);
- final Charset outputEncoding = Charset.forName(this.outputEncoding);
final String currLocale = locale != null ? locale : getDefaultLocale();
return new Settings(
configuration,
- args,
+ commandLineArgs,
templateDirectories,
- templateNames,
- interactiveTemplate,
- templateFileIncludePattern,
- templateFileExcludePattern,
- inputEncoding,
- outputEncoding,
+ Charset.forName(this.templateEncoding),
+ outputGeneratorDefinitions,
+ sharedDataSources,
+ sharedDataModels,
+ sourceIncludePattern,
+ sourceExcludePattern,
+ Charset.forName(this.inputEncoding),
+ Charset.forName(this.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 b6e6fd0..ab3681e 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
@@ -16,17 +16,10 @@
*/
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.TemplateTransformationsBuilder;
-import org.apache.freemarker.generator.base.template.TemplateTransformationsSupplier;
-
-import java.util.Map;
-import java.util.function.Supplier;
/**
* Convenience methods to create suppliers.
@@ -34,11 +27,7 @@
public class Suppliers {
public static ConfigurationSupplier configurationSupplier(Settings settings) {
- return new ConfigurationSupplier(settings, templateLoaderSupplier(settings));
- }
-
- public static ConfigurationSupplier configurationSupplier(Settings settings, Supplier<TemplateLoader> templateLoader) {
- return new ConfigurationSupplier(settings, templateLoader);
+ return new ConfigurationSupplier(settings, templateLoaderSupplier(settings), toolsSupplier(settings));
}
public static TemplateDirectorySupplier templateDirectorySupplier(String additionalTemplateDirName) {
@@ -53,30 +42,19 @@
return new ToolsSupplier(settings.getConfiguration(), settings.toMap());
}
- public static DataSourcesSupplier dataSourcesSupplier(Settings settings) {
- return new DataSourcesSupplier(settings.getDataSources(),
- settings.getDataSourceIncludePattern(),
- settings.getDataSourceExcludePattern(),
+ public static OutputGeneratorsSupplier outputGeneratorsSupplier(Settings settings) {
+ return new OutputGeneratorsSupplier(settings);
+ }
+
+ public static DataSourcesSupplier sharedDataSourcesSupplier(Settings settings) {
+ return new DataSourcesSupplier(settings.getSharedDataSources(),
+ settings.getSourceIncludePattern(),
+ settings.getSourceExcludePattern(),
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 TemplateTransformationsSupplier templateTransformationsSupplier(Settings settings) {
- return () -> TemplateTransformationsBuilder.builder()
- .setTemplate(Location.INTERACTIVE, settings.getInteractiveTemplate())
- .addSources(settings.getTemplates())
- .addInclude(settings.getTemplateFileIncludePattern())
- .addExclude(settings.getTemplateFileExcludePattern())
- .addOutputs(settings.getOutputs())
- .setWriter(settings.getWriter())
- .build();
+ public static DataModelSupplier sharedDataModelSupplier(Settings settings) {
+ return new DataModelSupplier(settings.getSharedDataModels());
}
public static PropertiesSupplier propertiesSupplier(String fileName) {
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ToolsSupplier.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ToolsSupplier.java
index 34efb5f..cb0e4d5 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ToolsSupplier.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/config/ToolsSupplier.java
@@ -16,20 +16,19 @@
*/
package org.apache.freemarker.generator.cli.config;
+import org.apache.freemarker.generator.base.FreeMarkerConstants.Configuration;
import org.apache.freemarker.generator.base.FreeMarkerConstants.Model;
import org.apache.freemarker.generator.base.tools.ToolsFactory;
+import org.apache.freemarker.generator.base.util.PropertiesTransformer;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.function.Supplier;
+import java.util.stream.Stream;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toMap;
-import static java.util.stream.Stream.of;
-import static org.apache.freemarker.generator.base.FreeMarkerConstants.Configuration.TOOLS_PREFIX;
-import static org.apache.freemarker.generator.base.util.PropertiesTransformer.filterKeyPrefix;
-import static org.apache.freemarker.generator.base.util.PropertiesTransformer.removeKeyPrefix;
/**
* Supplies FreeMarker tools based on the provided settings.
@@ -76,9 +75,9 @@
* @return tool properties
*/
private Properties toolsProperties() {
- return of(configuration)
- .map(p -> filterKeyPrefix(p, TOOLS_PREFIX))
- .map(p -> removeKeyPrefix(p, TOOLS_PREFIX))
+ return Stream.of(configuration)
+ .map(p -> PropertiesTransformer.filterKeyPrefix(p, Configuration.TOOLS_PREFIX))
+ .map(p -> PropertiesTransformer.removeKeyPrefix(p, Configuration.TOOLS_PREFIX))
.findFirst().get();
}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/DataSourcesModel.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/DataSourcesModel.java
deleted file mode 100644
index 45fa479..0000000
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/DataSourcesModel.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.apache.freemarker.generator.cli.model;
-
-import freemarker.ext.beans.BeanModel;
-import freemarker.ext.beans.BeansWrapper;
-import org.apache.freemarker.generator.base.datasource.DataSource;
-import org.apache.freemarker.generator.base.datasource.DataSources;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * Wraps an instance of <code>DataSources</code> into a more user-friendly <code>BeanModel</code>
- * so the user can use FreeMarker directives and features instead of using the exposed methods.
- */
-public class DataSourcesModel extends BeanModel {
-
- public DataSourcesModel(DataSources dataSources, BeansWrapper objectWrapper) {
- super(new SimpleDataSourcesAdapter(dataSources), requireNonNull(objectWrapper));
- }
-
- private static final class SimpleDataSourcesAdapter {
-
- private final DataSources dataSources;
-
- public SimpleDataSourcesAdapter(DataSources dataSources) {
- this.dataSources = dataSources;
- }
-
- public DataSource get(int index) {
- return dataSources.get(index);
- }
-
- public DataSource get(String name) {
- return dataSources.get(name);
- }
-
- public int size() {
- return dataSources.size();
- }
- }
-}
diff --git a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/GeneratorObjectWrapper.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/GeneratorObjectWrapper.java
index 57b9867..289611f 100644
--- a/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/GeneratorObjectWrapper.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/model/GeneratorObjectWrapper.java
@@ -1,3 +1,19 @@
+/*
+ * 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.model;
import freemarker.template.DefaultMapAdapter;
diff --git a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataModelDefinition.java
similarity index 64%
copy from freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java
copy to freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataModelDefinition.java
index ef06672..0c4ffcf 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataModelDefinition.java
@@ -6,7 +6,7 @@
* (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
+ * 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,
@@ -14,9 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.freemarker.generator.base.template;
+package org.apache.freemarker.generator.cli.picocli;
-import java.util.function.Supplier;
+import picocli.CommandLine.Option;
-public interface TemplateTransformationsSupplier extends Supplier<TemplateTransformations> {
+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-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataSourceDefinition.java
similarity index 65%
rename from freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java
rename to freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataSourceDefinition.java
index ef06672..61e9924 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/DataSourceDefinition.java
@@ -6,7 +6,7 @@
* (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
+ * 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,
@@ -14,9 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.freemarker.generator.base.template;
+package org.apache.freemarker.generator.cli.picocli;
-import java.util.function.Supplier;
+import picocli.CommandLine.Option;
-public interface TemplateTransformationsSupplier extends Supplier<TemplateTransformations> {
+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/OutputGeneratorDefinition.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/OutputGeneratorDefinition.java
new file mode 100644
index 0000000..78352ec
--- /dev/null
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/OutputGeneratorDefinition.java
@@ -0,0 +1,112 @@
+/*
+ * 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 (templateSourceDefinition == null) {
+ throw new ParameterException(commandLine, "No template defined to be rendered");
+ }
+
+ 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 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-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateOutputDefinition.java
similarity index 63%
copy from freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java
copy to freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateOutputDefinition.java
index ef06672..59627e3 100644
--- a/freemarker-generator-base/src/main/java/org/apache/freemarker/generator/base/template/TemplateTransformationsSupplier.java
+++ b/freemarker-generator-cli/src/main/java/org/apache/freemarker/generator/cli/picocli/TemplateOutputDefinition.java
@@ -6,7 +6,7 @@
* (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
+ * 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,
@@ -14,9 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.freemarker.generator.base.template;
+package org.apache.freemarker.generator.cli.picocli;
-import java.util.function.Supplier;
+import picocli.CommandLine.Option;
-public interface TemplateTransformationsSupplier extends Supplier<TemplateTransformations> {
+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 3842508..1fe6f57 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,42 +20,30 @@
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.template.TemplateTransformations;
-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.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.io.Writer;
-import java.net.URI;
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
-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;
+import static org.apache.freemarker.generator.base.FreeMarkerConstants.Model;
/**
* Renders a FreeMarker template.
@@ -64,105 +52,83 @@
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<TemplateTransformations> templateTransformationsSupplier;
+ private final Supplier<List<OutputGenerator>> outputGeneratorsSupplier;
+ private final Supplier<Map<String, Object>> sharedDataModelSupplier;
+ private final Supplier<List<DataSource>> sharedDataSourcesSupplier;
+ private final Supplier<Map<String, Object>> sharedParametersSupplier;
-
- 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<TemplateTransformations> 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);
+ public FreeMarkerTask(Supplier<Configuration> configurationSupplier,
+ Supplier<List<OutputGenerator>> outputGeneratorsSupplier,
+ Supplier<Map<String, Object>> sharedDataModelSupplier,
+ Supplier<List<DataSource>> sharedDataSourcesSupplier,
+ Supplier<Map<String, Object>> sharedParametersSupplier) {
+ this.configurationSupplier = requireNonNull(configurationSupplier, "configurationSupplier");
+ this.outputGeneratorsSupplier = requireNonNull(outputGeneratorsSupplier, "outputGeneratorsSupplier");
+ this.sharedDataModelSupplier = requireNonNull(sharedDataModelSupplier, "sharedDataModelSupplier");
+ this.sharedDataSourcesSupplier = requireNonNull(sharedDataSourcesSupplier, "sharedDataSourcesSupplier");
+ this.sharedParametersSupplier = requireNonNull(sharedParametersSupplier, "parametersSupplier");
}
@Override
public Integer call() {
- try {
- final Configuration configuration = configurationSupplier.get();
- final TemplateTransformations 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));
- 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();
+ final List<DataSource> sharedDataSources = sharedDataSourcesSupplier.get();
+ final Map<String, Object> sharedParameters = sharedParametersSupplier.get();
+
+ outputGenerators.forEach(outputGenerator -> process(
+ configuration,
+ outputGenerator,
+ sharedDataModel,
+ sharedDataSources,
+ sharedParameters));
+
+ 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,
+ List<DataSource> sharedDataSources,
+ Map<String, Object> sharedParameters) {
+ final TemplateSource templateSource = outputGenerator.getTemplateSource();
+ final TemplateOutput templateOutput = outputGenerator.getTemplateOutput();
+ final DataSources dataSources = toDataSources(outputGenerator, sharedDataSources);
+ final Map<String, Object> variables = outputGenerator.getVariables();
+ final Map<String, Object> templateDataModel = toTemplateDataModel(dataSources, variables, sharedDataModelMap, sharedParameters);
+
try (Writer writer = writer(templateOutput)) {
final Template template = template(configuration, templateSource);
- template.process(dataModel, writer);
+ template.process(templateDataModel, writer);
} catch (TemplateException | IOException e) {
throw new RuntimeException("Failed to process template: " + templateSource.getName(), e);
}
}
- 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 DataSources toDataSources(OutputGenerator outputGenerator, List<DataSource> sharedDataSources) {
+ return new DataSources(Stream.of(outputGenerator.getDataSources(), sharedDataSources)
+ .flatMap(Collection::stream).collect(Collectors.toList()));
}
- 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> toTemplateDataModel(DataSources dataSources, Map<String, Object>... maps) {
final Map<String, Object> result = new HashMap<>();
- result.putAll(dataModelsSupplier.get());
- result.put(DATASOURCES, dataSources);
- result.putAll(parameterModelSupplier.get());
- result.putAll(tools.get());
+ Arrays.stream(maps).forEach(result::putAll);
+ result.put(Model.DATASOURCES, dataSources);
return result;
}
- // ==============================================================
-
private static Writer writer(TemplateOutput templateOutput) throws IOException {
- if (templateOutput.getWriter() != null) {
+ if (templateOutput.hasWriter()) {
return templateOutput.getWriter();
+ } else {
+ final File file = templateOutput.getFile();
+ FileUtils.forceMkdirParent(file);
+ // We need to explicitly set our output encoding here - see https://freemarker.apache.org/docs/pgui_misc_charset.html
+ return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), templateOutput.getCharset()));
}
-
- final File file = templateOutput.getFile();
- FileUtils.forceMkdirParent(file);
- return new BufferedWriter(new FileWriter(file));
}
/**
@@ -177,12 +143,12 @@
*/
private static Template template(Configuration configuration, TemplateSource templateSource) {
switch (templateSource.getOrigin()) {
- case PATH:
+ case TEMPLATE_LOADER:
return fromTemplatePath(configuration, templateSource);
- case CODE:
+ case TEMPLATE_CODE:
return fromTemplateCode(configuration, templateSource);
default:
- throw new IllegalArgumentException("Don't know how to handle: " + templateSource.getOrigin());
+ throw new IllegalArgumentException("Don't know how to create a template: " + templateSource.getOrigin());
}
}
@@ -197,7 +163,6 @@
private static Template fromTemplateCode(Configuration configuration, TemplateSource templateSource) {
final String name = templateSource.getName();
-
try {
return new Template(name, templateSource.getCode(), configuration);
} catch (IOException e) {
diff --git a/freemarker-generator-cli/src/site/markdown/cli/advanced/cli-configuration.md b/freemarker-generator-cli/src/site/markdown/cli/advanced/cli-configuration.md
index bb8b8ca..092a5cb 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/advanced/cli-configuration.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/advanced/cli-configuration.md
@@ -16,7 +16,6 @@
# See https://freemarker.apache.org/docs/api/freemarker/template/Configuration.html#setSetting-java.lang.String-java.lang.String-
#############################################################################
# freemarker.configuration.setting.locale=JVM default
-
#############################################################################
# Configure FreeMarker Tools (name -> implementation class)
#############################################################################
@@ -27,7 +26,8 @@
freemarker.tools.freemarker=org.apache.freemarker.generator.tools.freemarker.FreeMarkerTool
freemarker.tools.grok=org.apache.freemarker.generator.tools.grok.GrokTool
freemarker.tools.gson=org.apache.freemarker.generator.tools.gson.GsonTool
-freemarker.tools.jsonpath=org.apache.freemarker.generator.tools.json.JsonPathTool
+freemarker.tools.javafaker=org.apache.freemarker.generator.tools.javafaker.JavaFakerTool
+freemarker.tools.jsonpath=org.apache.freemarker.generator.tools.jsonpath.JsonPathTool
freemarker.tools.jsoup=org.apache.freemarker.generator.tools.jsoup.JsoupTool
freemarker.tools.properties=org.apache.freemarker.generator.tools.properties.PropertiesTool
freemarker.tools.system=org.apache.freemarker.generator.tools.system.SystemTool
@@ -71,7 +71,6 @@
FreeMarker Generator Template Loader Directories
------------------------------------------------------------------------------
-[#1] /Users/sgoeschl
-[#2] /Users/sgoeschl/.freemarker-generator
-[#3] /Applications/Java/freemarker-generator-2.0.0
+[#1] /Users/sgoeschl/.freemarker-generator/templates
+[#2] /Applications/Java/freemarker-generator/templates
```
diff --git a/freemarker-generator-cli/src/site/markdown/cli/concepts/data-sources.md b/freemarker-generator-cli/src/site/markdown/cli/concepts/data-sources.md
index 53dd6bf..e6e4b3e 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/data-sources.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/data-sources.md
@@ -83,6 +83,17 @@
URI : file:/Users/sgoeschl/work/github/apache/freemarker-generator/freemarker-generator-cli/target/appassembler/examples/data/json/swagger-spec.json```
```
+Access to `stdin` is implemented as `DataSource` - please not that `stdin` is read lazy to cater for arbitrary large input data
+
+```
+cat examples/data/csv/contract.csv | bin/freemarker-generator -t freemarker-generator/info.ftl --stdin
+
+FreeMarker Generator DataSources
+------------------------------------------------------------------------------
+[#1]: name=stdin, group=default, fileName=stdin mimeType=text/plain, charset=UTF-8, length=-1 Bytes
+URI : system:///stdin
+```
+
### Selecting A DataSource
After loading one or more `DataSource` they are accessible as `dataSource` map in the FreeMarker model
diff --git a/freemarker-generator-cli/src/site/markdown/cli/concepts/design-goals.md b/freemarker-generator-cli/src/site/markdown/cli/concepts/design-goals.md
index 0d2e07c..1cf4634 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/design-goals.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/design-goals.md
@@ -13,5 +13,6 @@
* XML & XPath is supported by FreeMarker [out-of-the-box](http://freemarker.org/docs/xgui.html)
* Support for reading a data source content from STDIN to integrate with command line tools
* Support execution of arbitrary commands using [Apache Commons Exec](https://commons.apache.org/proper/commons-exec/)
+* Support creation of test data using [JavaFaker](https://github.com/DiUS/java-faker/)
* Add some commonly useful information such as `System Properties`, `Enviroment Variables`
* Support embedding the code in existing applications
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/site/markdown/cli/concepts/passing-data.md b/freemarker-generator-cli/src/site/markdown/cli/concepts/passing-data.md
index d181c01..3ff42cc 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/passing-data.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/passing-data.md
@@ -5,7 +5,7 @@
* System properties
* Parameters
-### User-Supplied System Poperties
+### User-Supplied System Properties
User-supplied system properties are added to the JVM's system properties
diff --git a/freemarker-generator-cli/src/site/markdown/cli/concepts/template-loading.md b/freemarker-generator-cli/src/site/markdown/cli/concepts/template-loading.md
index 9de70b6..5b1ce99 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/template-loading.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/template-loading.md
@@ -10,7 +10,6 @@
`Apache FreeMarker Generator CLI` uses a `MultiTemplateLoader` searching for templates in the following directories
-* Current working directory
* Optional `~/.freemarker-generator` directory
* `Apache FreeMarker Generator` installation directory
@@ -21,9 +20,8 @@
FreeMarker Generator Template Loader Directories
------------------------------------------------------------------------------
-[#1] /Users/sgoeschl/work/github/apache/freemarker-generator
-[#2] /Users/sgoeschl/.freemarker-generator
-[#3] /Applications/Java/freemarker-generator-2.0.0
+[#1] /Users/sgoeschl/.freemarker-generator/templates
+[#2] /Applications/Java/freemarker-generator/templates
```
The main benefit of `MultiTemplateLoader` is the use of abstract template paths finding a template in the template loader directories
@@ -38,16 +36,28 @@
<#import "/lib/commons-csv.ftl" as csv />
```
+You can add a template directory, e.g. the current working directory
+
+```
+freemarker-generator -t freemarker-generator/info.ftl --template-dir=./
+
+FreeMarker Generator Template Loader Directories
+------------------------------------------------------------------------------
+[#1] /Users/sgoeschl
+[#2] /Users/sgoeschl/.freemarker-generator/templates
+[#3] /Applications/Java/freemarker-generator/templates
+```
+
### Free-Style Template Loading
-The previously described `Template Loaders` do not support absolute template files or arbitrary URLS - this behaviour
+The previously described `Template Loaders` do not support absolute template files or arbitrary URLs - this behaviour
stems from security aspects when running `Apache FreeMarker` on the server side. For a command-line tool this is mostly
irrelevant therefore any template file outside of the template loader directories can be loaded
This example loads the `info.ftl` directly from a GitHub URL
```
-freemarker-generator -t https://raw.githubusercontent.com/apache/freemarker-generator/master/freemarker-generator-cli/templates/info.ftl
+freemarker-generator -t https://raw.githubusercontent.com/apache/freemarker-generator/master/freemarker-generator-cli/src/app/templates/freemarker-generator/info.ftl
```
### Interactive Template Loading
diff --git a/freemarker-generator-cli/src/site/markdown/cli/concepts/tools.md b/freemarker-generator-cli/src/site/markdown/cli/concepts/tools.md
index c5bb4da..e4a34c1 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/tools.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/tools.md
@@ -4,19 +4,20 @@
The following tools are currently implemented
-| Tool | Description |
-|-----------------------|-----------------------------------------------------------------------------------------------------------|
-| CSVTool | Process CSV files using [Apache Commons CSV](https://commons.apache.org/proper/commons-csv/) |
-| DataFrameTool | Bridge to [nRo/DataFrame](https://github.com/nRo/DataFrame) |
-| ExecTool | Execute command line tools using [Apache Commons Exec](https://commons.apache.org/proper/commons-exec/) |
-| ExcelTool | Process Excels files (XLS, XLSX) using [Apache POI](https://poi.apache.org) |
-| FreeMarkerTool | Expose useful FreeMarker classes |
-| GrokTool | Process text files using [Grok](https://github.com/thekrakken/java-grok) instead of regular expressions |
-| GsonTool | Process JSON files using [GSON](https://github.com/google/gson) |
-| JsonPathTool | Process JSON file using [Java JSON Path](https://github.com/json-path/JsonPath) |
-| JsoupTool | Processing HTML files using [Jsoup](https://jsoup.org) |
-| PropertiesTool | Process JDK properties files |
-| SystemTool | System-related utility methods |
-| UUIDTool | Create UUIDs |
-| XmlTool | Process XML files using [Apache FreeMarker](https://freemarker.apache.org/docs/xgui.html) |
-| YamlTool | Process YAML files using [SnakeYAML](https://bitbucket.org/asomov/snakeyaml/wiki/Home) |
+| Implementation Class | Name | Description |
+|-----------------------|---------------|-----------------------------------------------------------------------------------------------------------|
+| CSVTool | csv | Process CSV files using [Apache Commons CSV](https://commons.apache.org/proper/commons-csv/) |
+| DataFrameTool | dataframe | Bridge to [nRo/DataFrame](https://github.com/nRo/DataFrame) |
+| ExcelTool | excel | Process Excels files (XLS, XLSX) using [Apache POI](https://poi.apache.org) |
+| ExecTool | exec | Execute command line tools using [Apache Commons Exec](https://commons.apache.org/proper/commons-exec/) |
+| FreeMarkerTool | freemarker | Expose useful FreeMarker classes |
+| GrokTool | grok | Process text files using [Grok](https://github.com/thekrakken/java-grok) instead of regular expressions |
+| GsonTool | gson | Process JSON files using [GSON](https://github.com/google/gson) |
+| JavaFakerTool | javafaker | Generate test data using Java Faker [JavaFaker](https://github.com/DiUS/java-faker)
+| JsonPathTool | jsonpath | Process JSON file using [Java JSON Path](https://github.com/json-path/JsonPath) |
+| JsoupTool | jsoup | Processing HTML files using [Jsoup](https://jsoup.org) |
+| PropertiesTool | properties | Process JDK properties files |
+| SystemTool | system | System-related utility methods |
+| UUIDTool | uuid | Create UUIDs |
+| XmlTool | xaml | Process XML files using [Apache FreeMarker](https://freemarker.apache.org/docs/xgui.html) |
+| YamlTool | yaml | Process YAML files using [SnakeYAML](https://bitbucket.org/asomov/snakeyaml/wiki/Home) |
diff --git a/freemarker-generator-cli/src/site/markdown/cli/concepts/transformation.md b/freemarker-generator-cli/src/site/markdown/cli/concepts/transformation.md
index 03a9b3a..11b1323 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/concepts/transformation.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/concepts/transformation.md
@@ -3,30 +3,29 @@
The `freemarker-generator` generates text output based on processing FreeMarker templates and data
* A command line invocation requires 1..n `templates` and 0..n `data sources` / `data models`
-* A command line invocation is mapped to a series of `template transformations`
-* The `transformation` consists of exactly one `template`, 0..n `data sources` / `data models` written to an `output`
-* The `output` is either written to
+* A command line invocation is internally mapped to a list of `output generators`
+* The `output generator` consists of exactly one `template`, 0..n `data sources` / `data models` written to an `output` destination
+* The `output` of an `output generator` is either written to
* `stdout`
- * one or more output files
- * one or output directories
+ * an output file
+ * an output directory
* When the output is written to a directory
* the structure of the input directory is preserved
- * a `ftl` file extension is removed
+ * any `ftl` file extension is removed
+* Positional command line arguments are interpreted as `data sources` (or directories) and accessible by a `output generators`
### Examples
-Transforming a single template to a single output file
+Transform a single template to a single output file
+
+```
+freemarker-generator -t freemarker-generator/csv/md/transform.ftl -o target/contract.md examples/data/csv/contract.csv
+```
+
+Transform multiple templates to multiple output files (1:1 mapping between templates and outputs)
```
freemarker-generator \
--t freemarker-generator/csv/md/transform.ftl examples/data/csv/contract.csv \
--o target/contract.md
-```
-
-Transforming multiple templates to multiple output files (1:1 mapping between templates and outputs)
-
-```
-> freemarker-generator \
-t freemarker-generator/csv/md/transform.ftl -o target/contract.md \
-t freemarker-generator/csv/html/transform.ftl -o target/contract.html \
examples/data/csv/contract.csv
@@ -37,7 +36,7 @@
`-- contract.md
```
-Transforming single template directory to single output directory
+Transform a single template directory to single output directory
```
> freemarker-generator \
@@ -49,8 +48,6 @@
|-- application.properties
`-- nginx
`-- nginx.conf
-
-
```
Transforming multiple template directories to multiple output directories
@@ -70,5 +67,16 @@
|-- application.properties
`-- nginx
`-- nginx.conf
+```
+
+Transforming multiple templates and data sources to multiple output files
```
+freemarker-generator \
+-t freemarker-generator/yaml/json/transform.ftl -s examples/data/yaml/customer.yaml -o customer.json \
+-t freemarker-generator/yaml/json/transform.ftl -s examples/data/yaml/swagger-spec.yaml -o swagger-spec.json
+
+> ls -l *.json
+-rw-r--r-- 1 sgoeschl staff 332B Jan 5 21:30 customer.json
+-rw-r--r-- 1 sgoeschl staff 25K Jan 5 21:30 swagger-spec.json
+```
diff --git a/freemarker-generator-cli/src/site/markdown/cli/introduction/getting-started.md b/freemarker-generator-cli/src/site/markdown/cli/introduction/getting-started.md
index f1bc312..833fd37 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/introduction/getting-started.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/introduction/getting-started.md
@@ -12,7 +12,7 @@
On my local box (Mac OS 10.15.5) I use the following setup
```
-export FREEMARKER_CLI_HOME=/Applications/Java/freemarker-generator-2.0.0
+export FREEMARKER_CLI_HOME=/Applications/Java/freemarker-generator
export PATH=$PATH:$FREEMARKER_CLI_HOME/bin
```
@@ -20,14 +20,14 @@
```
> which freemarker-generator
-/Applications/Java/freemarker-generator-2.0.0/bin/freemarker-generator
+/Applications/Java/freemarker-generator/bin/freemarker-generator
```
and check the version of `Apache FreeMarker Generator`
```
> freemarker-generator -V
-version=0.1.0-SNAPSHOT, time=2020-06-25T21:48:02+0200, commit=b320d00094be8789086ad6153d9d3fcaf4b8c75f
+version=0.1.0-SNAPSHOT, time=2021-01-09T10:41:01+0100, commit=d308ede197f1c2972e3b328b9a37fa233cae101a
```
### Command Line Options
@@ -36,27 +36,32 @@
```
> freemarker-generator -h
-Usage: freemarker-generator (-t=<templates> [-t=<templates>]... |
- -i=<interactiveTemplate>) [-hV] [--stdin]
- [--config=<configFile>]
+Usage: freemarker-generator [-hV] [--stdin] [--config=<configFile>]
[--data-source-exclude=<dataSourceExcludePattern>]
[--data-source-include=<dataSourceIncludePattern>]
[-e=<inputEncoding>] [-l=<locale>]
[--output-encoding=<outputEncoding>]
- [--template-dir=<templateDir>] [--times=<times>]
- [-D=<String=String>]... [-m=<dataModels>]...
- [-o=<outputs>]... [-P=<String=String>]...
- [-s=<dataSources>]... [<sources>...]
+ [--template-dir=<templateDir>]
+ [--template-encoding=<templateEncoding>]
+ [--times=<times>] [-D=<String=String>]...
+ [-P=<String=String>]...
+ [--shared-data-model=<sharedDataModels>]...
+ ((-t=<template> | -i=<interactiveTemplate>)
+ [[--template-include=<templateIncludePatterns>]...
+ [--template-exclude=<templateExcludePatterns>]...]
+ [[-o=<outputs>]...] [[-s=<dataSources>]...]
+ [[-m=<dataModels>]...])... [<sharedDataSources>...]
Apache FreeMarker Generator
- [<sources>...] data source files and/or directories
+ [<sharedDataSources>...]
+ shared data source files and/or directories
--config=<configFile>
FreeMarker Generator configuration file
-D, --system-property=<String=String>
set system property
--data-source-exclude=<dataSourceExcludePattern>
- file exclude pattern for data sources
+ data source exclude pattern
--data-source-include=<dataSourceIncludePattern>
- file include pattern for data sources
+ data source include pattern
-e, --input-encoding=<inputEncoding>
encoding of data source
-h, --help Show this help message and exit.
@@ -72,11 +77,19 @@
set parameter
-s, --data-source=<dataSources>
data source used for rendering
+ --shared-data-model=<sharedDataModels>
+ shared data models used for rendering
--stdin read data source from stdin
- -t, --template=<templates>
+ -t, --template=<template>
templates to process
--template-dir=<templateDir>
additional template directory
+ --template-encoding=<templateEncoding>
+ template encoding
+ --template-exclude=<templateExcludePatterns>
+ template exclude pattern
+ --template-include=<templateIncludePatterns>
+ template include pattern
--times=<times> re-run X times for profiling
-V, --version Print version information and exit.
```
@@ -87,26 +100,25 @@
to better understand `Apache FreeMarker Generator`
```
-> freemarker-generator -t freemarker-generator/info.ftl
+> freemarker-generator -t freemarker-generator/info.ftl
FreeMarker Generator Information
------------------------------------------------------------------------------
FreeMarker version : 2.3.30
-Template name : info.ftl
+Template name : freemarker-generator/info.ftl
Language : en
Locale : en_US
-Timestamp : Nov 9, 2020 1:13:21 PM
+Timestamp : Jan 7, 2021 11:36:26 PM
Output encoding : UTF-8
Output format : plainText
FreeMarker Generator Template Loader Directories
------------------------------------------------------------------------------
[#1] /Users/sgoeschl/.freemarker-generator/templates
-[#2] /Users/sgoeschl/work/github/apache/freemarker-generator/freemarker-generator-cli/target/appassembler/templates
+[#2] /Applications/Java/freemarker-generator/templates
FreeMarker Generator Data Model
---------------------------------------------------------------------------
- dataSources
-- tools
FreeMarker Generator Tools
------------------------------------------------------------------------------
diff --git a/freemarker-generator-cli/src/site/markdown/cli/usage/generating-testdata.md b/freemarker-generator-cli/src/site/markdown/cli/usage/generating-testdata.md
index f19714d..4019eba 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/usage/generating-testdata.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/usage/generating-testdata.md
@@ -5,30 +5,32 @@
Let's assume that you need to populate a user table based on a CSV file with random data
```
-freemarker-generator -DNR_OF_RECORDS=10 -t examples/templates/javafaker/csv/testdata.ftl
+freemarker-generator -DNR_OF_RECORDS=10 -t examples/templates/javafaker/csv/testdata.ftl; echo
```
will generate
```
ID,CUSTOMER_ID,FIRST_NAME,LAST_NAME,EMAIL,IBAN,CREATED_AT
-5c3dbf2b-2957-41fe-8566-e16c91d6bba7,e6044780,Audrey,Ryan,audrey.ryan@gmail.com,DE69137185182464804325,2016-07-25T03:06:54+02:00
-34671167-92f8-46e3-874b-d488960eb320,z8394366,Herb,Lehner,herb.lehner@gmail.com,DE32993443443552974345,2019-10-11T23:27:32+02:00
-479855f6-cc98-4c46-99e4-b1d38d35a7b2,x8937857,Kirby,Wilkinson,kirby.wilkinson@gmail.com,DE12566901129220359287,2020-04-12T04:49:00+02:00
-1a3c51b9-d168-4c0a-84ae-05e79cd181b1,t3486957,Charmaine,Bergstrom,charmaine.bergstrom@gmail.com,DE98964063811726229158,2015-07-24T23:19:19+02:00
-43b9f7ad-1aec-44ff-b3c1-de7688b5a729,z9190225,Sterling,Glover,sterling.glover@gmail.com,DE47207633748672977993,2019-11-04T04:45:06+01:00
-34ce2c9f-e5bb-44f4-a71f-40b0dfa8d0bf,a4406167,George,Marquardt,george.marquardt@gmail.com,DE79342449317255392445,2016-05-15T16:33:05+02:00
-1f9bbc16-8b17-4947-ab50-4abf6aa4cc46,s6438445,Arnoldo,Herzog,arnoldo.herzog@gmail.com,DE20421444995381411375,2013-10-12T07:01:01+02:00
-30e3f7a2-7fe8-4ebf-b46b-4f59ab62ba45,o9507275,Nickie,Predovic,nickie.predovic@gmail.com,DE06666930299990216198,2019-08-01T10:51:51+02:00
-f703e93e-7bc3-42c9-a7f5-f1db84d32fd1,z8385157,Clinton,Murphy,clinton.murphy@gmail.com,DE27305002168865903990,2018-04-01T19:03:55+02:00
-7f6a8d29-2dfc-4467-b366-25b46aa5bc32,x5244747,Johnson,Blanda,johnson.blanda@gmail.com,DE83757301199253406795,2012-06-23T18:04:38+02:00
+ec48407f-b74b-3255-aa23-c478b97fe6a0,k1627756,Finnja,Huebel,finnja.huebel@server.invalid,DE34649925979537623502,2019-02-02T06:19:32+01:00
+4386bbd1-f410-310f-9961-3e8b2bdfb158,k2697194,Catharina,Ruckdeschel,catharina.ruckdeschel@server.invalid,DE20946378663346781874,2016-01-08T21:31:30+01:00
+5225a437-a981-33f8-be17-4520d1656357,i2477693,Efe,Grün,efe.grün@server.invalid,DE72530958450307406958,2014-01-27T19:33:54+01:00
+f3d7cbd6-b7ba-3709-97e3-a0f8c66f3a61,h1789606,Adriano,Walz,adriano.walz@server.invalid,DE91943313948716057559,2019-01-06T18:57:24+01:00
+3b43d134-8342-322d-8814-2efb8d2525af,x9067951,Denny,Kleininger,denny.kleininger@server.invalid,DE60996500459835447795,2014-10-30T19:07:59+01:00
+0f145471-293f-32c4-8dd2-9be95540e5f6,k9415677,Silas,Bönisch,silas.bönisch@server.invalid,DE53857572315572131803,2014-07-15T15:33:06+02:00
+ec0a21b7-106b-3983-8b19-03bf91f5fa33,a0190000,Ceyda,Schäffel,ceyda.schäffel@server.invalid,DE83184389823488369676,2020-07-28T21:16:33+02:00
+9c0d0feb-dd08-3cbd-875e-ea9f072d1a96,w7084669,Wilhelm,Burmeister,wilhelm.burmeister@server.invalid,DE89066581983817613534,2017-09-26T04:01:56+02:00
+76f770a9-4008-317e-800f-6c203dd363d8,f7683995,Madlen,Knobel,madlen.knobel@server.invalid,DE82012476109671707669,2018-07-08T05:52:25+02:00
+6ecf87f6-6a46-3f06-9482-ab2ffe696c41,y8814536,Jessica,Koubaa,jessica.koubaa@server.invalid,DE79396597066674926625,2019-10-12T23:55:02+02:00
+4a72d322-55db-35ee-b47e-c484d29b5760,v1849194,Jesper,Hildenbrand,jesper.hildenbrand@server.invalid,DE60616233632021309556,2015-03-09T01:37:18+01:00
```
using the following FreeMarker template
```
-<#assign faker = tools.javafaker.faker>
-<#assign nrOfRecords = tools.system.getString("NR_OF_RECORDS","10")>
+<#-- Get a localized JavaFaker instance -->
+<#assign faker = tools.javafaker.getFaker("de_DE")>
+<#assign nrOfRecords = tools.system.getString("NR_OF_RECORDS","100")>
<#assign days = tools.javafaker.timeUnits["DAYS"]>
<#assign csvTargetFormat = tools.csv.formats["DEFAULT"].withFirstRecordAsHeader()>
<#assign csvPrinter = tools.csv.printer(csvTargetFormat)>
@@ -37,15 +39,18 @@
<#if csvTargetFormat.getSkipHeaderRecord()>
${csvPrinter.printRecord(csvHeaders)}<#t>
</#if>
- <#list 1..nrOfRecords?number as i>
- <#assign id = tools.uuid.randomUUID()>
+ <#list 0..nrOfRecords?number as i>
+ <#-- Generate a reproducable id to allow re-importing of test data -->
+ <#assign id = tools.uuid.namedUUID("trxid-" + i?string)>
<#assign customerId = faker.bothify("?#######")>
<#assign firstName = faker.name().firstName()>
<#assign lastName = faker.name().lastName()>
- <#assign email = firstName + "." + lastName + "@gmail.com">
+ <#assign email = firstName + "." + lastName + "@server.invalid">
+ <#-- JavaFakers IBAN generation is really slow -->
<#assign iban = faker.finance().iban("DE")>
-
+ <#-- Distribute the creation date up to 10 years in the past -->
<#assign createAt = faker.date().past(3650, days)>
+ <#-- Use a CSV Printer to properly escape the output -->
${csvPrinter.printRecord(
id,
customerId,
@@ -61,6 +66,7 @@
Some thoughts along the line
* [Java Faker](https://github.com/DiUS/java-faker) does not create coherent test data, e.g. each invocation of "name" creates a new random name - hence we create the email address ourselves
-* The created IBAN does not use a valid bank code but structure and checksum is correct
+* The created IBAN does not use a valid bank code but structure and checksum is correct (albeit slow)
* The "createdAt" generates a creation date from the last 10 years to have some proper distribution
-* See [A Guide to JavaFaker](https://www.baeldung.com/java-faker) for a quick overview
\ No newline at end of file
+* See [A Guide to JavaFaker](https://www.baeldung.com/java-faker) for a quick overview
+* A CSV Printer is used to properly escape the generated fields (in case they contain a CSV separator)
\ No newline at end of file
diff --git a/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-directories.md b/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-directories.md
index 2aa4e7f..247d9a8 100644
--- a/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-directories.md
+++ b/freemarker-generator-cli/src/site/markdown/cli/usage/transforming-directories.md
@@ -34,12 +34,20 @@
index index.htm;
```
-### Transform Template Directory To STDOUT
+### Transform Template Directory To Output Directory
-If no output directory is provided all output is written to `stdout`
+The transformed templates are written to an `out` directory
+
+* `nginx.conf.ftl` was changed to `nginx.conf" during the transformation
```
-freemarker-generator -t examples/data/template/
+freemarker-generator -t examples/data/template/ -o out; tree out; cat out/application.properties out/nginx/nginx.conf
+out
+|-- application.properties
+`-- nginx
+ `-- nginx.conf
+
+1 directory, 2 files
# == application.properties ==================================================
server.name=127.0.0.1
server.logs=/var/log/nginx
@@ -53,35 +61,21 @@
}
```
-### Transform Template Directory To Output Directory
-
-The transformed templates are written to an `out` directory
-
-* `nginx.conf.ftl` was changed to `nginx.conf" during the transformation
-
-```
-freemarker-generator -t examples/data/template/ -o out; tree out
-out
-|-- application.properties
-`-- nginx
- `-- nginx.conf
-
-1 directory, 2 files
-```
-
### Use Command Line Parameters
A user-supplied parameter `NGINX_HOSTNAME` is used to render the templates
```
-freemarker-generator -t examples/data/template/ -P NGINX_HOSTNAME=localhost
+freemarker-generator -t examples/data/template/ -P NGINX_HOSTNAME=some.host.invalid -o out; \
+cat out/application.properties out/nginx/nginx.conf
+
# == application.properties ==================================================
-server.name=localhost
+server.name=some.host.invalid
server.logs=/var/log/nginx
# == nginx-conf ==============================================================
server {
listen 80;
- server_name localhost;
+ server_name some.host.invalid;
root /usr/share/nginx/www;
index index.htm;
@@ -136,15 +130,15 @@
Another option is passing the information as JSON file
```
-echo '{"NGINX_PORT":"8443","NGINX_HOSTNAME":"localhost"}' > nginx.json
+echo '{"NGINX_PORT":"8443","NGINX_HOSTNAME":"some.host.invalid"}' > nginx.json
freemarker-generator -t examples/data/template/ -m nginx.json
# == application.properties ==================================================
-server.name=localhost
+server.name=some.host.invalid
server.logs=/var/log/nginx
# == nginx-conf ==============================================================
server {
listen 8443;
- server_name localhost;
+ server_name some.host.invalid;
root /usr/share/nginx/www;
index index.htm;
@@ -156,7 +150,7 @@
Yet another option is using a YAML file
```
-echo -e "- NGINX_PORT": "\"8443\"\n- NGINX_HOSTNAME": "localhost" > nginx.yaml
+echo -e "- NGINX_PORT": "\"8443\"\n- NGINX_HOSTNAME": "some.host.invalid" > nginx.yaml
freemarker-generator -t examples/data/template/ -m nginx.yaml
# == application.properties ==================================================
server.name=localhost
@@ -179,15 +173,15 @@
* `#mimeType=application/json` defines that JSON content is parsed
```
-export NGINX_CONF='{"NGINX_PORT":"8443","NGINX_HOSTNAME":"localhost"}'
+export NGINX_CONF='{"NGINX_PORT":"8443","NGINX_HOSTNAME":"some.host.invalid"}'
freemarker-generator -t examples/data/template/ -m env:///NGINX_CONF#mimeType=application/json
# == application.properties ==================================================
-server.name=localhost
+server.name=some.host.invalid
server.logs=/var/log/nginx
# == nginx-conf ==============================================================
server {
listen 8443;
- server_name localhost;
+ server_name some.host.invalid;
root /usr/share/nginx/www;
index index.htm;
@@ -199,7 +193,7 @@
For testing purpose it is useful to override certain settings
```
-export NGINX_CONF='{"NGINX_PORT":"8443","NGINX_HOSTNAME":"localhost"}'
+export NGINX_CONF='{"NGINX_PORT":"8443","NGINX_HOSTNAME":"some.host.invalid"}'
freemarker-generator -t examples/data/template/ -PNGINX_HOSTNAME=www.mydomain.com -m env:///NGINX_CONF#mimeType=application/json
# == application.properties ==================================================
server.name=www.mydomain.com
diff --git a/freemarker-generator-cli/src/site/markdown/index.md b/freemarker-generator-cli/src/site/markdown/index.md
index 9a6ecc4..e6cde71 100644
--- a/freemarker-generator-cli/src/site/markdown/index.md
+++ b/freemarker-generator-cli/src/site/markdown/index.md
@@ -9,9 +9,9 @@
* Transform a directory with a single command-line invocation
* Made for the heavy lifting of data by using lazy-loading and streaming
-### Getting Started
+### First Seps
-* [Installation](cli/introduction/getting-started.html)
+* [Getting Started](cli/introduction/getting-started.html)
* [Running Examples](cli/usage/running-examples.html)
### Concepts
diff --git a/freemarker-generator-cli/src/test/data/encoding/utf16.txt b/freemarker-generator-cli/src/test/data/encoding/utf16.txt
index 4d8e1f2..6dfd2e5 100755
--- a/freemarker-generator-cli/src/test/data/encoding/utf16.txt
+++ b/freemarker-generator-cli/src/test/data/encoding/utf16.txt
Binary files differ
diff --git a/freemarker-generator-cli/src/test/data/encoding/utf8.txt b/freemarker-generator-cli/src/test/data/encoding/utf8.txt
index c43eb00..25f1e5c 100755
--- a/freemarker-generator-cli/src/test/data/encoding/utf8.txt
+++ b/freemarker-generator-cli/src/test/data/encoding/utf8.txt
@@ -1,3 +1,5 @@
+UTF-8 encoding
+=============================================================================
première is first
première is slightly different
Кириллица is Cyrillic
diff --git a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
index 045bb42..5c5ebd5 100644
--- a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
+++ b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/ExamplesTest.java
@@ -148,6 +148,16 @@
}
@Test
+ public void shouldTransformMultipleTemplatesAndDataSources() throws IOException {
+ final String output = execute(
+ "-t freemarker-generator/yaml/json/transform.ftl -s src/app/examples/data/yaml/swagger-spec.yaml -o swagger-spec.json " +
+ "-t freemarker-generator/yaml/json/transform.ftl -s src/app/examples/data/yaml/customer.yaml -o customer.json");
+
+ assertTrue("Swagger file content is missing", output.contains("This is a sample server Petstore server"));
+ assertTrue("Customer data is missing", output.contains("Xyz, DEF Street"));
+ }
+
+ @Test
public void shouldSupportDataSourcesAccessInFTL() throws IOException {
final String args = "users=src/app/examples/data/json/github-users.json contract=src/app/examples/data/csv/contract.csv";
@@ -161,22 +171,6 @@
assertEquals("application/json", execute(args + " -i ${dataSources[\"users\"].mimeType}"));
}
- /**
- * @Test public void shouldNotShadowDataSourcesInFTL() throws IOException {
- * final String args = "empty=examples/data/json/github-users.json";
- * <p>
- * // check shadowing of "isEmpty"
- * assertEquals("false", execute("empty=examples/data/json/github-users.json -i ${dataSources.empty?c}"));
- * // DataSources#isEmpty shadows the data source "empty"
- * // assertEquals("false", execute("empty=examples/data/json/github-users.json -i ${DataSources[\"empty\"]}"));
- * assertEquals("empty", execute("empty=examples/data/json/github-users.json -i ${dataSources.get(\"empty\").name}"));
- * <p>
- * // check shadowing of "find"
- * // assertEquals("find", execute("find=examples/data/json/github-users.json -i ${dataSources.find.name}"));
- * // assertEquals("find", execute("find=examples/data/json/github-users.json -i ${DataSources[\"find\"].name}"));
- * }
- */
-
@Test
@Ignore("Manual test to check memory consumption and resource handling")
public void shouldCloseAllResources() throws IOException {
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..4a3a5b8 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,17 +16,19 @@
*/
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 {
private static final String ANY_TEMPLATE = "any.ftl";
- private static final String OTHER_TEMPLATE = "other.ftl";
private static final String INTERACTIVE_TEMPLATE = "interactive-template";
private static final String ANY_FILE = "users.csv";
private static final String OTHER_FILE = "transctions.csv";
@@ -35,52 +37,64 @@
@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.sharedDataSources.get(0));
}
@Test
public void shouldParseMultiplePositionalParameter() {
- assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, ANY_FILE, OTHER_FILE).sources.get(0));
- assertEquals(OTHER_FILE, parse("-t", ANY_TEMPLATE, ANY_FILE, OTHER_FILE).sources.get(1));
+ assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, ANY_FILE, OTHER_FILE).sharedDataSources.get(0));
+ assertEquals(OTHER_FILE, parse("-t", ANY_TEMPLATE, ANY_FILE, OTHER_FILE).sharedDataSources.get(1));
- assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, ANY_FILE, OTHER_FILE_URI).sources.get(0));
- assertEquals(OTHER_FILE_URI, parse("-t", ANY_TEMPLATE, ANY_FILE, OTHER_FILE_URI).sources.get(1));
+ assertEquals(ANY_FILE, parse("-t", ANY_TEMPLATE, ANY_FILE, OTHER_FILE_URI).sharedDataSources.get(0));
+ assertEquals(OTHER_FILE_URI, parse("-t", ANY_TEMPLATE, ANY_FILE, OTHER_FILE_URI).sharedDataSources.get(1));
- assertEquals(ANY_FILE_URI, parse("-t", ANY_TEMPLATE, ANY_FILE_URI, OTHER_FILE_URI).sources.get(0));
- assertEquals(OTHER_FILE_URI, parse("-t", ANY_TEMPLATE, ANY_FILE_URI, OTHER_FILE_URI).sources.get(1));
+ assertEquals(ANY_FILE_URI, parse("-t", ANY_TEMPLATE, ANY_FILE_URI, OTHER_FILE_URI).sharedDataSources.get(0));
+ assertEquals(OTHER_FILE_URI, parse("-t", ANY_TEMPLATE, ANY_FILE_URI, OTHER_FILE_URI).sharedDataSources.get(1));
}
@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));
- assertNull(main.sources);
+ assertEquals(ANY_FILE, main.outputGeneratorDefinitions.get(0).getDataSources().get(0));
+ assertEquals(OTHER_FILE_URI, main.outputGeneratorDefinitions.get(0).getDataSources().get(1));
+ assertNull(main.sharedDataSources);
}
@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));
- assertNull(main.sources);
+ assertEquals(ANY_FILE, outputGeneratorDefinition.getDataModels().get(0));
+ assertEquals(OTHER_FILE_URI, outputGeneratorDefinition.getDataModels().get(1));
+ assertNull(main.sharedDataSources);
}
@Test
@@ -102,25 +116,64 @@
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());
+
+ assertEquals("template01.ftl", defs.get(0).templateSourceDefinition.template);
+ assertEquals(1, defs.get(0).dataSourceDefinition.dataSources.size());
+ assertEquals("datasource10.csv", defs.get(0).dataSourceDefinition.dataSources.get(0));
+ assertNull(defs.get(0).templateOutputDefinition);
+
+ assertEquals("template02.ftl", defs.get(1).templateSourceDefinition.template);
+ assertEquals(2, defs.get(1).dataSourceDefinition.dataSources.size());
+ assertEquals("datasource20.csv", defs.get(1).dataSourceDefinition.dataSources.get(0));
+ assertEquals("datasource21.csv", defs.get(1).dataSourceDefinition.dataSources.get(1));
+ assertNull(defs.get(0).templateOutputDefinition);
+
+ assertEquals("some-interactive-template01", defs.get(2).templateSourceDefinition.interactiveTemplate);
+ assertEquals(1, defs.get(2).dataSourceDefinition.dataSources.size());
+ assertEquals("datasource30.csv", defs.get(2).dataSourceDefinition.dataSources.get(0));
+ assertEquals("out.txt", defs.get(2).templateOutputDefinition.outputs.get(0));
+
+ assertEquals("some-interactive-template02", defs.get(3).templateSourceDefinition.interactiveTemplate);
}
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/ConfigurationSupplierTest.java b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/ConfigurationSupplierTest.java
index 93ddfa2..67721b9 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
@@ -51,7 +51,7 @@
}
private ConfigurationSupplier configurationSupplier(Settings settings) {
- return new ConfigurationSupplier(settings, templateLoaderSupplier());
+ return new ConfigurationSupplier(settings, templateLoaderSupplier(), Suppliers.toolsSupplier(settings));
}
private TemplateLoaderSupplier templateLoaderSupplier() {
@@ -60,7 +60,6 @@
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
deleted file mode 100644
index cbd838a..0000000
--- a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SettingsTest.java
+++ /dev/null
@@ -1,86 +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.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 {
-
- private static final String[] ANY_ARGS = singletonList("arg1 arg2").toArray(new String[0]);
- private static final Properties ANY_CONFIGURATION = new Properties();
- private static final String ANY_INCLUDE = "*.csv";
- private static final String ANY_INPUT_ENCODING = "US-ASCII";
- private static final String ANY_INTERACTIVE_TEMPLATE = "interactiveTemplate";
- private static final String ANY_LOCALE = "th-TH";
- private static final String ANY_OUTPUT_ENCODING = "UTF-16";
- private static final String ANY_OUTPUT_FILE = "outputFile";
- private static final List<String> ANY_SOURCES = singletonList("sources");
- private static final String ANY_TEMPLATE_NAME = "templateName";
- 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();
-
- 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)
- .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);
- }
-}
diff --git a/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SuppliersTest.java b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SuppliersTest.java
new file mode 100644
index 0000000..c59c955
--- /dev/null
+++ b/freemarker-generator-cli/src/test/java/org/apache/freemarker/generator/cli/config/SuppliersTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.config;
+
+import freemarker.cache.TemplateLoader;
+import freemarker.template.Configuration;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.freemarker.generator.base.FreeMarkerConstants.Model;
+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.TemplateSource.Origin;
+import org.apache.freemarker.generator.base.util.OperatingSystem;
+import org.apache.freemarker.generator.cli.picocli.DataModelDefinition;
+import org.apache.freemarker.generator.cli.picocli.DataSourceDefinition;
+import org.apache.freemarker.generator.cli.picocli.OutputGeneratorDefinition;
+import org.apache.freemarker.generator.cli.picocli.TemplateSourceDefinition;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+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.assertNull;
+import static org.junit.Assert.assertTrue;
+
+@SuppressWarnings("unchecked")
+public class SuppliersTest {
+
+ private static final String ANY_DATA_MODEL_NAME = "src/test/data/properties/test.properties";
+ private static final String ANY_DATA_SOURCE_NAME = "src/test/data/properties/test.properties";
+ private static final String ANY_INTERACTIVE_TEMPLATE = "Hello World";
+ private static final String ANY_TEMPLATE_DIRECTORY_NAME = "src/test/templates";
+ private static final String ANY_TEMPLATE_NAME = "echo.ftl";
+ private static final List<File> ANY_TEMPLATE_DIRECTORIES = singletonList(new File(ANY_TEMPLATE_DIRECTORY_NAME));
+
+ @Test
+ public void shouldCreateTemplateDirectorySupplier() {
+ final TemplateDirectorySupplier templateDirectorySupplier = Suppliers.templateDirectorySupplier(ANY_TEMPLATE_DIRECTORY_NAME);
+
+ final List<File> files = templateDirectorySupplier.get();
+
+ assertTrue(files.get(0).getAbsolutePath().endsWith(fixSeparators(ANY_TEMPLATE_DIRECTORY_NAME)));
+ }
+
+ @Test
+ public void shouldCreateTools() {
+ final Properties configuration = new Properties();
+ configuration.setProperty("freemarker.tools.system", "org.apache.freemarker.generator.tools.system.SystemTool");
+ final Settings settings = Settings.builder().setConfiguration(configuration).build();
+ final ToolsSupplier toolsSupplier = Suppliers.toolsSupplier(settings);
+
+ final Map<String, Object> tools = (Map<String, Object>) toolsSupplier.get().get(Model.TOOLS);
+
+ assertEquals(1, tools.size());
+ assertNotNull(tools.get("system"));
+ }
+
+ @Test
+ public void shouldCreateTemplateLoaderSupplier() throws IOException {
+ final Settings settings = Settings.builder().setTemplateDirectories(ANY_TEMPLATE_DIRECTORIES).build();
+ final TemplateLoaderSupplier templateLoaderSupplier = Suppliers.templateLoaderSupplier(settings);
+
+ final TemplateLoader templateLoader = templateLoaderSupplier.get();
+
+ assertNotNull(templateLoader.findTemplateSource(ANY_TEMPLATE_NAME));
+ }
+
+ @Test
+ public void shouldCreateConfiguration() {
+ final Settings settings = Settings.builder().build();
+ final ConfigurationSupplier configurationSupplier = Suppliers.configurationSupplier(settings);
+
+ final Configuration configuration = configurationSupplier.get();
+
+ assertNotNull(configuration.getSharedVariable(Model.TOOLS));
+ assertTrue(configuration.isTemplateLoaderExplicitlySet());
+ assertTrue(configuration.isObjectWrapperExplicitlySet());
+ }
+
+ @Test
+ public void shouldCreateSharedDataSources() {
+ final Settings settings = Settings.builder().setSharedDataSources(singletonList(ANY_DATA_SOURCE_NAME)).build();
+ final DataSourcesSupplier dataSourcesSupplier = Suppliers.sharedDataSourcesSupplier(settings);
+
+ final List<DataSource> dataSourceList = dataSourcesSupplier.get();
+
+ assertEquals(1, dataSourceList.size());
+ assertTrue(dataSourceList.get(0).getName().endsWith(ANY_DATA_SOURCE_NAME));
+ }
+
+ @Test
+ public void shouldCreateSharedDataModel() {
+ final Settings settings = Settings.builder().setSharedDataModels(singletonList(ANY_DATA_MODEL_NAME)).build();
+ final DataModelSupplier sharedDataModelSupplier = Suppliers.sharedDataModelSupplier(settings);
+
+ final Map<String, Object> map = sharedDataModelSupplier.get();
+
+ assertEquals(2, map.size());
+ }
+
+ @Test
+ public void shouldCreateOutputGenerator() {
+ final OutputGeneratorDefinition def = new OutputGeneratorDefinition();
+ def.templateSourceDefinition = new TemplateSourceDefinition();
+ def.templateSourceDefinition.interactiveTemplate = ANY_INTERACTIVE_TEMPLATE;
+ def.dataModelDefinition = new DataModelDefinition();
+ def.dataModelDefinition.dataModels = singletonList(ANY_DATA_MODEL_NAME);
+ def.dataSourceDefinition = new DataSourceDefinition();
+ def.dataSourceDefinition.dataSources = singletonList(ANY_DATA_SOURCE_NAME);
+ final Settings settings = Settings.builder().setOutputGeneratorDefinitions(singletonList(def)).build();
+ final OutputGeneratorsSupplier outputGeneratorsSupplier = Suppliers.outputGeneratorsSupplier(settings);
+
+ final List<OutputGenerator> outputGenerators = outputGeneratorsSupplier.get();
+ final OutputGenerator outputGenerator = outputGenerators.get(0);
+
+ assertEquals(1, outputGenerators.size());
+ assertEquals(Origin.TEMPLATE_CODE, outputGenerator.getTemplateSource().getOrigin());
+ assertEquals(1, outputGenerator.getDataSources().size());
+ assertEquals(2, outputGenerator.getVariables().size());
+ assertEquals("foo", outputGenerator.getVariables().get("FOO"));
+ assertNotNull(outputGenerator.getTemplateOutput().getWriter());
+ assertNull(outputGenerator.getTemplateOutput().getFile());
+ }
+ private static String fixSeparators(String str) {
+ if (OperatingSystem.isWindows()) {
+ return FilenameUtils.separatorsToWindows(str);
+ } else {
+ return str;
+ }
+ }
+
+}
diff --git a/freemarker-generator-maven-plugin/src/test/java/org/apache/freemarker/generator/maven/OperatingSystem.java b/freemarker-generator-maven-plugin/src/test/java/org/apache/freemarker/generator/maven/OperatingSystem.java
index 9c974c2..4575238 100644
--- a/freemarker-generator-maven-plugin/src/test/java/org/apache/freemarker/generator/maven/OperatingSystem.java
+++ b/freemarker-generator-maven-plugin/src/test/java/org/apache/freemarker/generator/maven/OperatingSystem.java
@@ -1,3 +1,19 @@
+/*
+ * 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.maven;
import java.util.Locale;
diff --git a/freemarker-generator-tools/pom.xml b/freemarker-generator-tools/pom.xml
index 97a2dfd..635b9c1 100644
--- a/freemarker-generator-tools/pom.xml
+++ b/freemarker-generator-tools/pom.xml
@@ -109,7 +109,7 @@
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
- <version>2.4.0</version>
+ <version>2.5.0</version>
</dependency>
<!-- JsoupTool -->
<dependency>
diff --git a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
index 552ae16..3490ee8 100644
--- a/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
+++ b/freemarker-generator-tools/src/main/java/org/apache/freemarker/generator/tools/commonscsv/CommonsCSVTool.java
@@ -50,7 +50,7 @@
public List<CSVParser> parse(Collection<DataSource> dataSources) {
return dataSources.stream()
.map(this::parse)
- .collect(Collectors.toList());
+ .collect(toList());
}
public CSVParser parse(DataSource dataSource, CSVFormat format) {
diff --git a/licences/LICENCE_javafaker.txt b/licences/LICENCE_javafaker.txt
new file mode 100644
index 0000000..896c78c
--- /dev/null
+++ b/licences/LICENCE_javafaker.txt
@@ -0,0 +1,13 @@
+Copyright 2014 DiUS Computing
+
+Licensed 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.
\ No newline at end of file