/*
* ******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2016, 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.templateclassgenerator.codegen;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import de.monticore.ast.ASTNode;
import de.monticore.generating.ExtendedGeneratorEngine;
import de.monticore.generating.GeneratorSetup;
import de.monticore.generating.templateengine.GlobalExtensionManagement;
import de.monticore.templateclassgenerator.EmptyNode;
import de.se_rwth.commons.Names;
import freemarker.cache.FileTemplateLoader;
import freemarker.core.FMHelper;
import freemarker.core.Parameter;
import freemarker.template.Configuration;
import freemarker.template.Template;
/**
* This class generates a template class for each template.
*
* @author Jerome Pfeiffer
*/
public class TemplateClassGenerator {
/**
* Generates the template fqnTemplateName from the modelPath to the
* targetFilePath with the targetName
*
* @param targetName
* @param modelPath
* @param fqnTemplateName
* @param targetFilepath
*/
public static void generateClassForTemplate(String targetName, Path modelPath,
String fqnTemplateName,
File targetFilepath) {
List<Parameter> params = new ArrayList<>();
Optional<String> result = Optional.empty();
Configuration config = new Configuration();
Template t = null;
boolean hasSignature = false;
try {
config.setTemplateLoader(new FileTemplateLoader(modelPath.toFile()));
t = config.getTemplate(fqnTemplateName);
}
catch (IOException e) {
e.printStackTrace();
}
Map<String, List<List<String>>> methodCalls = FMHelper.getMethodCalls(t);
if (methodCalls.containsKey(TemplateClassGeneratorConstants.PARAM_METHOD)) {
// we just recognize the first entry as there
// must not be multiple params definitions
hasSignature = true;
params = FMHelper
.getParams(methodCalls.get(TemplateClassGeneratorConstants.PARAM_METHOD).get(0));
}
if (methodCalls.containsKey(TemplateClassGeneratorConstants.RESULT_METHOD)) {
// A template can only have one result type.
String dirtyResult = methodCalls.get(TemplateClassGeneratorConstants.RESULT_METHOD).get(0)
.get(0);
String cleanResult = dirtyResult.replace("\"", "");
result = Optional.of(cleanResult);
}
doGenerateTemplateClass(targetFilepath, fqnTemplateName, targetName, params, result,
hasSignature);
}
/**
* Does the generation with the parameters of the signature method
* tc.params(...) and tc.signature(...).
*
* @param targetFilepath
* @param fqnTemplateName
* @param targetName
* @param params
* @param result
*/
private static void doGenerateTemplateClass(File targetFilepath, String fqnTemplateName,
String targetName,
List<Parameter> params, Optional<String> result, boolean hasSignature) {
final GeneratorSetup setup = new GeneratorSetup(targetFilepath);
GlobalExtensionManagement glex = new GlobalExtensionManagement();
glex.setGlobalValue("TemplateClassPackage",
TemplateClassGeneratorConstants.TEMPLATE_CLASSES_PACKAGE);
glex.setGlobalValue("TemplateClassSetupPackage",
TemplateClassGeneratorConstants.TEMPLATE_CLASSES_SETUP_PACKAGE);
setup.setGlex(glex);
TemplateClassHelper helper = new TemplateClassHelper();
final ExtendedGeneratorEngine generator = new ExtendedGeneratorEngine(setup);
ASTNode node = new EmptyNode();
String packageNameWithSeperators = TemplateClassGeneratorConstants.TEMPLATE_CLASSES_PACKAGE
+ File.separator
+ Names.getPathFromFilename(fqnTemplateName);
String packageNameWithDots = Names.getPackageFromPath(packageNameWithSeperators);
boolean isMainTemplate = targetName.endsWith("Main");
if (isMainTemplate) {
generateMainTemplateFactory(generator, packageNameWithSeperators, targetName,
node, packageNameWithDots);
}
generator.generate("typesafety.TemplateClass",
Paths.get(packageNameWithSeperators, targetName + ".java"), node,
packageNameWithDots, fqnTemplateName, targetName, params, result, hasSignature,
isMainTemplate, helper);
}
public static void generateMainTemplateFactory(ExtendedGeneratorEngine generator,
String packageNameWithSeperators, String targetName, ASTNode node,
String packageNameWithDots) {
generator.generate("typesafety.MainTemplateFactory",
Paths.get(packageNameWithSeperators, targetName + "Factory.java"), node, targetName,
packageNameWithDots);
}
/**
* Generates a TemplateStorage class, which contains all generated template
* classes. Further it generates a generator config class to configure the
* used generator engine in template classes and a setup template to configure
* the static use of template classes within a template.
*
* @param foundTemplates
* @param targetFilepath
* @param modelPath
* @param foundTemplates
*/
public static void generateTemplateSetup(File targetFilepath, File modelPath,
List<String> foundTemplates) {
String packageName = TemplateClassGeneratorConstants.TEMPLATE_CLASSES_PACKAGE + "."
+ TemplateClassGeneratorConstants.TEMPLATE_CLASSES_SETUP_PACKAGE;
final GeneratorSetup setup = new GeneratorSetup(targetFilepath);
setup.setTracing(false);
GlobalExtensionManagement glex = new GlobalExtensionManagement();
glex.setGlobalValue("TemplatePostfix",
TemplateClassGeneratorConstants.TEMPLATE_CLASSES_POSTFIX);
glex.setGlobalValue("TemplateClassPackage",
TemplateClassGeneratorConstants.TEMPLATE_CLASSES_PACKAGE);
glex.setGlobalValue("TemplatesAlias", TemplateClassGeneratorConstants.TEMPLATES_ALIAS);
setup.setGlex(glex);
final ExtendedGeneratorEngine generator = new ExtendedGeneratorEngine(setup);
String basedir = getBasedirFromModelAndTargetPath(modelPath.getAbsolutePath(),
targetFilepath.getAbsolutePath());
String relativePath = getRelativePathFromAbsolute(basedir, targetFilepath.getAbsolutePath());
if (relativePath.contains(File.separator)) {
relativePath = relativePath.replace(File.separator, "/");
}
String filePath = Names.getPathFromPackage(packageName) + File.separator;
String mp = modelPath.getPath();
List<File> nodes = TemplateClassHelper.walkTree(modelPath);
List<String> templates = foundTemplates;
generator.generate("typesafety.setup.TemplateAccessor",
Paths.get(filePath + "TemplateAccessor.java"),
new EmptyNode(),
packageName, templates, mp, new TemplateClassHelper());
generator.generate("typesafety.setup.Setup", Paths.get(filePath + "Setup.ftl"),
new EmptyNode(),
nodes, mp,
new TemplateClassHelper(), new ArrayList<File>());
generator.generate("typesafety.setup.GeneratorConfig",
Paths.get(filePath + "GeneratorConfig.java"),
new EmptyNode(), packageName, relativePath);
}
/**
* Compares the two paths and returns the common path. The common path is the
* basedir.
*
* @param modelPath
* @param targetPath
* @return
*/
private static String getBasedirFromModelAndTargetPath(String modelPath, String targetPath) {
String basedir = "";
for (int i = 0; i < modelPath.length(); i++) {
if (modelPath.charAt(i) == targetPath.charAt(i)) {
basedir += modelPath.charAt(i);
}
else {
break;
}
}
return basedir;
}
/**
* Replaces the basedir in the absolute Path -> path gets relative
*
* @param basedir
* @param absolutePath
* @return
*/
private static String getRelativePathFromAbsolute(String basedir, String absolutePath) {
if (absolutePath.contains(basedir)) {
return absolutePath.replace(basedir, "");
}
return absolutePath;
}
}