/*
* ******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2015, MontiCore, All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* ******************************************************************************
*/
package de.monticore.generating;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import de.monticore.ast.ASTNode;
import com.google.common.base.Joiner;
import de.monticore.generating.templateengine.GlobalExtensionManagement;
import de.monticore.generating.templateengine.ITemplateControllerFactory;
import de.monticore.generating.templateengine.TemplateController;
import de.monticore.generating.templateengine.TemplateControllerConfiguration;
import de.monticore.generating.templateengine.TemplateControllerConfigurationBuilder;
import de.monticore.generating.templateengine.TemplateControllerFactory;
import de.monticore.generating.templateengine.freemarker.FreeMarkerConfigurationBuilder;
import de.monticore.generating.templateengine.freemarker.FreeMarkerTemplateEngine;
import de.monticore.generating.templateengine.reporting.Reporting;
import de.monticore.io.FileReaderWriter;
import de.monticore.io.paths.IterablePath;
import de.se_rwth.commons.Names;
import de.se_rwth.commons.logging.Log;
import freemarker.template.Configuration;
/**
* Represents the whole generator engine component. Clients usually need only this class when
* generating.
*
* @author (last commit) $Author$
* @version $Revision$, $Date$
*/
public class GeneratorEngine {
private final TemplateControllerConfiguration templateControllerConfig;
private final ITemplateControllerFactory templateControllerFactory;
public final static String GENERATED_CLASS_SUFFIX = "TOP";
public GeneratorEngine(
GeneratorSetup generatorSetup,
ITemplateControllerFactory templateControllerFactory,
FileReaderWriter fileHandler) {
Log.errorIfNull(generatorSetup);
this.templateControllerConfig = createTemplateControllerConfiguration(generatorSetup,
templateControllerFactory, fileHandler);
this.templateControllerFactory = templateControllerConfig.getTemplateControllerFactory();
}
public GeneratorEngine(GeneratorSetup generatorSetup) {
this(generatorSetup, null, null);
}
/* package visibility */TemplateControllerConfiguration createTemplateControllerConfiguration(
GeneratorSetup generatorSetup, ITemplateControllerFactory templateControllerFactory,
FileReaderWriter fileHandler) {
if (templateControllerFactory == null) {
templateControllerFactory = TemplateControllerFactory.getInstance();
}
if (fileHandler == null) {
fileHandler = new FileReaderWriter();
}
Configuration freemarkerConfig = new FreeMarkerConfigurationBuilder()
.classLoader(generatorSetup.getClassLoader())
.additionalTemplatePaths(generatorSetup.getAdditionalTemplatePaths())
.autoImports(generatorSetup.getAutoTemplateImports())
.build();
GlobalExtensionManagement glex = generatorSetup.getGlex().orElse(
new GlobalExtensionManagement());
FreeMarkerTemplateEngine freeMarkerTemplateEngine = new FreeMarkerTemplateEngine(
freemarkerConfig);
TemplateControllerConfiguration tcConfig = new TemplateControllerConfigurationBuilder()
.glex(glex)
.templateControllerFactory(templateControllerFactory)
.classLoader(generatorSetup.getClassLoader())
.fileHandler(fileHandler)
.outputDirectory(generatorSetup.getOutputDirectory())
.freeMarkerTemplateEngine(freeMarkerTemplateEngine)
.tracing(generatorSetup.isTracing())
.commentStart(
generatorSetup.getCommentStart().orElse(
TemplateControllerConfigurationBuilder.DEFAULT_COMMENT_START))
.commentEnd(
generatorSetup.getCommentEnd().orElse(
TemplateControllerConfigurationBuilder.DEFAULT_COMMENT_END))
.modelName(generatorSetup.getModelName())
.build();
return tcConfig;
}
/**
* Processes the template <code>templateName</code> with the given <code>templateArguments</code>
* and returns the content as String.
*
* @param templateName the template to be processes
* @param templateArguments additional template arguments (if needed).
*/
public String generate(String templateName,
Object... templateArguments) {
TemplateController tc = this.templateControllerFactory.create(this.templateControllerConfig,
templateName);
return tc.includeArgs(templateName, Arrays.asList(templateArguments));
}
/**
* Processes the template <code>templateName</code> with the <code>node</code> and the given
* <code>templateArguments</code> and writes the content into the <code>filePath</code>. Note:
* Unless not absolute, the <code>filePath</code> is relative to the configured output directory
* specified in the {@link de.monticore.generating.GeneratorSetup}.
*
* @param templateName the template to be processes
* @param filePath the file path in which the content is to be written
* @param node the ast node
* @param templateArguments additional template arguments (if needed).
*/
public void generate(String templateName, Path filePath, ASTNode node,
Object... templateArguments) {
Log.errorIfNull(node);
checkArgument(!isNullOrEmpty(templateName));
Log.errorIfNull(filePath);
TemplateController tc = templateControllerFactory.create(templateControllerConfig, "");
tc.writeArgs(templateName, filePath, node, Arrays.asList(templateArguments));
}
/**
* Processes the template <code>templateName</code> with the <code>node</code> and the given
* <code>templateArguments</code> and writes the content into the <code>filePath</code>. If there
* is a handwritten file on the handcoded path, the suffix "TOP" is added to the name of the
* generated file. Note: Unless not absolute, the <code>filePath</code> is relative to the
* configured output directory specified in the {@link de.monticore.generating.GeneratorSetup}.
*
* @param templateName the template to be processes
* @param filePath the file path in which the content is to be written
* @param handcodedPath the path for the handwritten code
* @param node the ast node
* @param templateArguments additional template arguments (if needed)
*/
public void generateAndConsiderHWC(String templateName, Path filePath, IterablePath handcodedPath,
ASTNode node,
Object... templateArguments) {
Log.errorIfNull(filePath);
if (handcodedPath.exists(filePath)) {
Reporting.reportUseHandwrittenCodeFile(handcodedPath.getResolvedPath(filePath).get(),
filePath);
filePath = getPathIfHWCExists(filePath);
}
else {
Reporting.reportUseHandwrittenCodeFile(null, filePath);
}
generate(templateName, filePath, node, templateArguments);
}
/**
* Adds suffix "TOP" to the name of the file to generate
*
* @param filePath
* @return converted file path
*/
private Path getPathIfHWCExists(Path filePath) {
String fileName = filePath.getFileName().toString();
if (fileName.contains(".")) {
fileName = Joiner.on('.').join(Names.getQualifier(fileName) + GENERATED_CLASS_SUFFIX,
Names.getSimpleName(fileName));
}
else {
fileName = fileName + GENERATED_CLASS_SUFFIX;
}
return Paths.get(filePath.getParent().toString(), fileName);
}
}