blob: 37100aa7b1bd6605e13942ecc8dc3c199362d12d [file] [log] [blame]
/*
* 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.file.RecursiveFileSupplier;
import org.apache.freemarker.generator.base.util.NonClosableWriterWrapper;
import org.apache.freemarker.generator.base.util.StringUtils;
import org.apache.freemarker.generator.base.util.Validate;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
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.
*/
public class TemplateTransformationsBuilder {
/** Interactive template */
private TemplateSource template;
/** List of templates and/or template directories to be rendered */
private final List<String> sources;
/** Optional include patterns for resolving source templates or template directories */
private final List<String> includes;
/** Optional exclude patterns for resolving source templates or template directories */
private final List<String> excludes;
/** Optional output file or directory */
private final List<File> outputs;
/** Optional user-supplied writer */
private Writer writer;
private TemplateTransformationsBuilder() {
this.sources = new ArrayList<>();
this.includes = new ArrayList<>();
this.excludes = new ArrayList<>();
this.outputs = new ArrayList<>();
this.writer = null;
}
public static TemplateTransformationsBuilder builder() {
return new TemplateTransformationsBuilder();
}
public TemplateTransformations build() {
validate();
final List<TemplateTransformation> result = new ArrayList<>();
if (template != null) {
final File outputFile = outputs.isEmpty() ? null : outputs.get(0);
result.add(resolveInteractiveTemplate(outputFile));
} else {
for (int i = 0; i < sources.size(); i++) {
final String source = sources.get(i);
final File output = i < outputs.size() ? outputs.get(i) : null;
result.addAll(resolve(source, output));
}
}
return new TemplateTransformations(result);
}
public TemplateTransformationsBuilder setTemplate(String name, String code) {
if (StringUtils.isNotEmpty(code)) {
this.template = TemplateSource.fromCode(name, 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);
return this;
}
public TemplateTransformationsBuilder addInclude(String include) {
if (StringUtils.isNotEmpty(include)) {
this.includes.add(include);
}
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);
}
return this;
}
public TemplateTransformationsBuilder addExcludes(Collection<String> excludes) {
if (excludes != null) {
this.excludes.addAll(excludes);
}
return this;
}
public TemplateTransformationsBuilder addOutput(String output) {
if (StringUtils.isNotEmpty(output)) {
this.outputs.add(new File(output));
}
return this;
}
public TemplateTransformationsBuilder addOutput(File output) {
if (output != null) {
this.outputs.add(output);
}
return this;
}
public TemplateTransformationsBuilder addOutputs(Collection<String> outputs) {
if (outputs != null) {
outputs.forEach(this::addOutput);
}
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)));
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");
}
/**
* Resolve a <code>source</code> to a list of <code>TemplateTransformation</code>.
*
* @param source the source being a file name, an URI or <code>NamedUri</code>
* @param output Optional output file or directory
* @return list of <code>TemplateTransformation</code>
*/
private List<TemplateTransformation> resolve(String source, File output) {
if (isTemplateFile(source)) {
return resolveTemplateFile(source, output);
} else if (isTemplateDirectory(source)) {
return resolveTemplateDirectory(source, output);
} else if (isTemplatePath(source)) {
return resolveTemplatePath(source, output);
} else {
return resolveTemplateCode(source, output);
}
}
private List<TemplateTransformation> resolveTemplateFile(String source, File outputFile) {
final TemplateSource templateSource = templateSource(source);
final TemplateOutput templateOutput = templateOutput(outputFile);
return singletonList(new TemplateTransformation(templateSource, templateOutput));
}
private List<TemplateTransformation> resolveTemplateDirectory(String source, File outputDirectory) {
Validate.fileExists(new File(source), "Template directory does not exist: " + source);
final File templateDirectory = new File(source);
final List<File> templateFiles = templateFilesSupplier(source, getInclude(), getExclude()).get();
final List<TemplateTransformation> templateTransformations = new ArrayList<>();
for (File templateFile : templateFiles) {
final TemplateSource templateSource = templateSource(templateFile.getAbsolutePath());
final File outputFile = getTemplateOutputFile(templateDirectory, templateFile, outputDirectory);
final TemplateOutput templateOutput = templateOutput(outputFile);
templateTransformations.add(new TemplateTransformation(templateSource, templateOutput));
}
return templateTransformations;
}
private List<TemplateTransformation> resolveTemplatePath(String source, File out) {
final TemplateSource templateSource = templateSource(source);
final TemplateOutput templateOutput = templateOutput(out);
return singletonList(new TemplateTransformation(templateSource, templateOutput));
}
private TemplateTransformation resolveInteractiveTemplate(File out) {
final TemplateOutput templateOutput = templateOutput(out);
return new TemplateTransformation(template, templateOutput);
}
private List<TemplateTransformation> resolveTemplateCode(String source, File out) {
final TemplateSource templateSource = TemplateSource.fromCode("interactive", source);
final TemplateOutput templateOutput = templateOutput(out);
return singletonList(new TemplateTransformation(templateSource, templateOutput));
}
private TemplateOutput templateOutput(File templateOutputFile) {
if (writer == null && templateOutputFile != null) {
return TemplateOutput.fromFile(templateOutputFile);
} else {
return TemplateOutput.fromWriter(writer);
}
}
private TemplateSource templateSource(String source) {
return TemplateSourceFactory.create(source);
}
private String getInclude() {
return includes.isEmpty() ? null : includes.get(0);
}
private String getExclude() {
return excludes.isEmpty() ? null : excludes.get(0);
}
private Writer writer(String outputFile, String outputEncoding) {
try {
if (writer != null) {
return writer;
} else if (!StringUtils.isEmpty(outputFile)) {
return new BufferedWriter(new FileWriter(outputFile));
} else {
return new BufferedWriter(new OutputStreamWriter(System.out, outputEncoding));
}
} catch (IOException e) {
throw new RuntimeException("Unable to create writer", e);
}
}
private static File getTemplateOutputFile(File templateDirectory, File templateFile, File outputDirectory) {
final String relativePath = relativePath(templateDirectory, templateFile);
final String relativeOutputFileName = mapExtension(relativePath);
return new File(outputDirectory, relativeOutputFileName);
}
private static boolean isTemplateFile(String source) {
final File file = new File(source);
return file.exists() && file.isFile();
}
private static boolean isTemplateDirectory(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 RecursiveFileSupplier templateFilesSupplier(String source, String include, String exclude) {
return new RecursiveFileSupplier(singletonList(source), singletonList(include), singletonList(exclude));
}
private static String relativePath(File directory, File file) {
return file.getAbsolutePath()
.substring(directory.getAbsolutePath().length())
.substring(1);
}
private static String mapExtension(String fileName) {
if (fileName.toLowerCase().endsWith(".ftl")) {
return fileName.substring(0, fileName.length() - 4);
} else {
return fileName;
}
}
}