/*
* Copyright 2010 Pablo Arrighi, Alex Concha, Miguel Lezama for version 1.
* Copyright 2013 Pablo Arrighi, Miguel Lezama, Kevin Mazet for version 2.
*
* This file is part of GOOL.
*
* GOOL is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, version 3.
*
* GOOL 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 General Public License version 3 for more details.
*
* You should have received a copy of the GNU General Public License along with GOOL,
* in the file COPYING.txt. If not, see <http://www.gnu.org/licenses/>.
*/
package gool.generator.common;
import gool.ast.core.ClassDef;
import gool.ast.core.Dependency;
import gool.ast.core.Node;
import gool.ast.type.IType;
import gool.generator.GeneratorHelper;
import gool.generator.GoolGeneratorController;
import gool.generator.common.exception.VelocityException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import logger.Log;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
/**
* Provides the basic functionality to generate code from a list of GOOL
* classes. This class is used to parse the velocity templates depending of the
* target language.
*
* Note: Only one {@link CodeGenerator} at a time is supported.
*/
public abstract class CodePrinter {
/**
* The directory where the generated code will be written. Usually, this is
* that specified in gool.properties For a given target language But
* ultimately this is specified by Platform objects
*/
private File outputDir;
/**
* This list is just to remember which abstract GOOL classes were printed
* already.
*/
protected Set<ClassDef> printedClasses = new HashSet<ClassDef>();
/**
* the Velocity template engine.
*/
private VelocityEngine engine;
/**
* The selected code generator.
*/
private CodeGenerator generator;
private Collection<File> myFileToCopy;
/**
* Creates a new {@link CodePrinter} with a specific {@link CodeGenerator}.
*
* @param generator
* the {@link CodeGenerator} used to produce the code.
* @param outputDir
* @throws Exception
* @throws Exception
* when the velocity engine can not be properly initialized.
*/
public CodePrinter(CodeGenerator generator, File outputDir,
Collection<File> myFile) {
this.generator = generator;
this.engine = new VelocityEngine();
this.outputDir = outputDir;
this.myFileToCopy = myFile;
for (File fi : myFileToCopy) {
File fd;
String path = "";
try {
int i = fi.getCanonicalPath().lastIndexOf("src") + 4;
path = fi.getCanonicalPath().toString().substring(i);
} catch (IOException e) {
Log.e(e.toString());
}
if (path.length() != fi.getName().length()) {
int nbF = (path.length() - fi.getName().length() - 1);
String a = path.substring(0, nbF);
new File(outputDir + File.separator + a).mkdirs();
}
fd = new File(outputDir + File.separator + path);
try {
CopierFichier(fi, fd);
} catch (FileNotFoundException e1) {
System.out.println(e1.toString());
}
}
GoolGeneratorController.setCodeGenerator(generator);
try {
Properties p = new Properties();
p.setProperty("resource.loader", "class");
p.setProperty("class.resource.loader.class",
"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
engine.init(p);
} catch (Exception e) {
throw new VelocityException(
"The velocity engine can not be properly initialized.", e);
}
}
public CodePrinter(CodeGenerator generator, File outputDir) {
this.generator = generator;
this.engine = new VelocityEngine();
this.outputDir = outputDir;
GoolGeneratorController.setCodeGenerator(generator);
try {
Properties p = new Properties();
p.setProperty("resource.loader", "class");
p.setProperty("class.resource.loader.class",
"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
engine.init(p);
} catch (Exception e) {
throw new VelocityException(
"The velocity engine can not be properly initialized.", e);
}
}
/**
* Cleans the output directory.
*/
public void clean() {
File[] files = outputDir.listFiles();
if (files != null) {
for (File file : files) {
if (!file.isFile()) {
file.delete();
}
}
}
}
/**
* Gets the {@link CodeGenerator} associated to this printer.
*
* @return the {@link CodeGenerator} associated to this printer.
*/
public CodeGenerator getCodeGenerator() {
return generator;
}
/**
* Parses a Velocity template with its required parameters. Except for the
* top ClassDef.getCode() call triggered by the print(ClassDef) below all of
* the other getCode() calls will be triggered from here by velocity itself.
*
* @param template
* the relative path to a velocity template.
* @param classDef
* the abstract GOOL that will fill in the template.
* @return the code generated for the selected template.
* @throws Exception
* @throws ParseErrorException
* @throws ResourceNotFoundException
* @throws Exception
* when Velocity is unable to parse the template.
*/
public String processTemplate(String templateFilename, Node classDef) {
try {
// Load the template into velocity
String templateFile = getTemplateDir() + templateFilename;
Template template = engine.getTemplate(templateFile);
Log.i(String.format("Loaded velocity template: %s", templateFile));
// Provide velocity with what it needs to fill in the template
// i.e. the ClassDef
// but also some macros
// and helpful routines
VelocityContext context = new VelocityContext();
context.put("class", classDef);
context.put("macros", getTemplateDir() + "macros.vm");
context.put("Helper", GeneratorHelper.class);
// Go fill in template.
// A great quick introduction to velocity syntax is available
// Here:
// http://velocity.apache.org/engine/releases/velocity-1.5/user-guide.html
StringWriter writer = new StringWriter();
template.merge(context, writer);
return writer.toString();
} catch (ResourceNotFoundException e) {
throw e;
} catch (Exception e) {
throw new VelocityException(e.getLocalizedMessage(), e);
}
}
/**
* This is the main entry point of this class. It generates the code for a
* GOOL class and its corresponding dependent classes.
*
* @param pclass
* the class to generate.
* @return the list of files of generated concrete target classes
* @throws FileNotFoundException
* @throws Exception
* when the generated code cannot be written to the file system
* or when there is a parsing error in Velocity templates.
* Side-effects: updates the list of abstract GOOL classes that
* have been processed
*/
public List<File> print(ClassDef pclass) throws FileNotFoundException {
// GOOL library classes are printed in a different manner
if(pclass.isGoolLibraryClass()){
return printGoolLibraryClass(pclass);
}
/*
* Delegate the code generation to the ClassDef object, which may decide
* that the currentPrinter need be changed since platforms are decided
* on a per class basis
*/
String code = pclass.getCode();
// file separator is just a slash in Unix
// so the second argument to File() is just the directory
// that corresponds to the package name
// the first argument is the default output directory of the platform
// so the directory name ends up being something like
// GOOLOUPTUTTARGET/pack/age
File dir = new File(getOutputDir().getAbsolutePath(),
StringUtils.replace(pclass.getPackageName(), ".",
File.separator));
// Typically the outputdir was created before, but not the package
// subdirs
dir.mkdirs();
// Create the file for the class, fill it in, close it
File classFile = new File(dir, getFileName(pclass.getName()));
Log.i(String.format("Writing to file %s", classFile));
PrintWriter writer = new PrintWriter(classFile);
writer.println(code);
writer.close();
// Remember that you did the generation for this one abstract GOOL class
printedClasses.add(pclass);
// Put the generated class into the result list
List<File> result = new ArrayList<File>();
result.add(classFile);
// Go through the dependencies
// If they are abstract GOOL classes and have not been generated, do
// that
// And add the generated classes into the result list
for (Dependency dependency : pclass.getDependencies()) {
if (!printedClasses.contains(dependency)
&& dependency instanceof ClassDef) {
result.addAll(print((ClassDef) dependency));
}
}
return result;
}
public List<File> printGoolLibraryClass(ClassDef pclass) throws FileNotFoundException {
String goolClass = pclass.getPackageName()+"."+pclass.getName();
ArrayList<String> goolClassImplems = new ArrayList<String>();
for(String Import : GeneratorMatcher.matchImports(goolClass))
if(Import.startsWith("+"))
goolClassImplems.add(Import.substring(1));
List<File> result = new ArrayList<File>();
for(String goolClassImplem : goolClassImplems){
String goolClassImplemName = goolClassImplem.substring(goolClassImplem.lastIndexOf(".")+1);
String goolClassImplemPackage = goolClassImplem.substring(0, goolClassImplem.lastIndexOf("."));
String implemFileName = pclass.getPlatform().getCodePrinter().getFileName(goolClassImplemName);
String code = GeneratorMatcher.matchGoolClassImplementation(goolClass, implemFileName);
File dir = new File(getOutputDir().getAbsolutePath(),
StringUtils.replace(goolClassImplemPackage, ".",
File.separator));
dir.mkdirs();
File implemFile = new File(dir, implemFileName);
PrintWriter writer = new PrintWriter(implemFile);
writer.println(code);
writer.close();
}
printedClasses.add(pclass);
return result;
}
/**
* Gets the filename that should be used for the corresponding class. For
* example, Java forces the same name to be used for the file and the
* (public) class.
*
* @param className
* a class name.
* @return the correct filename for the specified class.
*/
public abstract String getFileName(String className);
/**
* Gets the path where the Velocity templates are located.
*
* @return the path where the velocity templates are located.
*/
public abstract String getTemplateDir();
/**
* Gets the output directory.
*
* @return the output directory.
*/
public File getOutputDir() {
return outputDir;
}
private static Map<Platform, CodePrinter> codePrinters = new HashMap<Platform, CodePrinter>();
public static void registerWriter(Platform platform, CodePrinter writer) {
codePrinters.put(platform, writer);
}
/**
* Each platform has specifies a CodePrinter. which itself has a
* CodeGenerator this returns that CodePrinter after having set its
* CodeGenerator in the GoolGeneratorController
*/
public static CodePrinter getPrinter(IType platform) {
CodePrinter codePrinter = ((Platform) platform).getCodePrinter();
if (codePrinter == null) {
throw new IllegalStateException(
String.format(
"There are no registered code writers for the specified platform: %s",
platform));
}
GoolGeneratorController
.setCodeGenerator(codePrinter.getCodeGenerator());
return codePrinter;
}
public void setOutputDir(File outputDir) {
this.outputDir = outputDir;
}
public static Set<Platform> getRegistredPlatforms() {
return codePrinters.keySet();
}
public Collection<File> print(Collection<ClassDef> generatedClassDefs,
boolean isGool) throws FileNotFoundException {
Collection<File> result = new ArrayList<File>();
for (ClassDef classDef : generatedClassDefs) {
result.addAll(print(classDef));
}
return result;
}
private boolean CopierFichier(File Source, File Destination)
throws FileNotFoundException {
boolean resultat = false;
FileInputStream filesource = null;
FileOutputStream fileDestination = null;
try {
filesource = new FileInputStream(Source);
fileDestination = new FileOutputStream(Destination);
byte buffer[] = new byte[512 * 1024];
int nblecture;
while ((nblecture = filesource.read(buffer)) != -1) {
fileDestination.write(buffer, 0, nblecture);
}
resultat = true;
} catch (FileNotFoundException nf) {
nf.printStackTrace();
} catch (IOException io) {
io.printStackTrace();
} finally {
try {
filesource.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
fileDestination.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return resultat;
}
public List<File> printPersonalLib() throws FileNotFoundException {
ArrayList<File> r = new ArrayList<File>();
return r;
}
public String processTemplate(String templateFilename, String className) {
try {
// Load the template into velocity
String templateFile = getTemplateDir() + templateFilename;
Template template = engine.getTemplate(templateFile);
VelocityContext context = new VelocityContext();
context.put("class", className);
context.put("macros", getTemplateDir() + "macros.vm");
context.put("Helper", GeneratorHelper.class);
StringWriter writer = new StringWriter();
template.merge(context, writer);
return writer.toString();
} catch (Exception e) {
throw new VelocityException(e.getLocalizedMessage(), e);
}
}
}