/*
* #%~
* Code Generator Plugin
* %%
* Copyright (C) 2008 - 2014 Overture
* %%
* This program 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, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #~%
*/
package org.overture.ide.plugins.javagen.commands;
import java.io.File;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.SystemUtils;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.handlers.HandlerUtil;
import org.osgi.service.prefs.Preferences;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.lex.Dialect;
import org.overture.codegen.analysis.vdm.Renaming;
import org.overture.codegen.analysis.violations.InvalidNamesResult;
import org.overture.codegen.analysis.violations.Violation;
import org.overture.codegen.assistant.AssistantManager;
import org.overture.codegen.assistant.LocationAssistantIR;
import org.overture.codegen.ir.IRSettings;
import org.overture.codegen.ir.IrNodeInfo;
import org.overture.codegen.ir.VdmNodeInfo;
import org.overture.codegen.utils.AnalysisExceptionIR;
import org.overture.codegen.utils.GeneralCodeGenUtils;
import org.overture.codegen.utils.GeneralUtils;
import org.overture.codegen.utils.GeneratedData;
import org.overture.codegen.utils.GeneratedModule;
import org.overture.codegen.vdm2java.IJavaConstants;
import org.overture.codegen.vdm2java.JavaCodeGen;
import org.overture.codegen.vdm2java.JavaCodeGenUtil;
import org.overture.codegen.vdm2java.JavaSettings;
import org.overture.codegen.vdm2jml.JmlGenerator;
import org.overture.codegen.vdm2jml.JmlSettings;
import org.overture.config.Settings;
import org.overture.ide.core.IVdmModel;
import org.overture.ide.core.resources.IVdmProject;
import org.overture.ide.plugins.javagen.Activator;
import org.overture.ide.plugins.javagen.CodeGenConsole;
import org.overture.ide.plugins.javagen.ICodeGenConstants;
import org.overture.ide.plugins.javagen.util.PluginVdm2JavaUtil;
import org.overture.ide.ui.utility.VdmTypeCheckerUi;
public class Vdm2JavaCommand extends AbstractHandler
{
private AssistantManager assistantManager;
public Vdm2JavaCommand()
{
this.assistantManager = new AssistantManager();
}
public Object execute(ExecutionEvent event) throws ExecutionException
{
// Validate project
ISelection selection = HandlerUtil.getCurrentSelection(event);
if (!(selection instanceof IStructuredSelection))
{
return null;
}
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
Object firstElement = structuredSelection.getFirstElement();
if (!(firstElement instanceof IProject))
{
return null;
}
final IProject project = (IProject) firstElement;
final IVdmProject vdmProject = (IVdmProject) project.getAdapter(IVdmProject.class);
try
{
Settings.release = vdmProject.getLanguageVersion();
Settings.dialect = vdmProject.getDialect();
} catch (CoreException e)
{
Activator.log("Problems setting VDM language version and dialect", e);
e.printStackTrace();
}
CodeGenConsole.GetInstance().activate();
CodeGenConsole.GetInstance().clearConsole();
deleteMarkers(project);
final IVdmModel model = vdmProject.getModel();
if (model == null)
{
CodeGenConsole.GetInstance().println("Could not get model for project: "
+ project.getName());
return null;
}
if (!model.isParseCorrect())
{
CodeGenConsole.GetInstance().println("Could not parse model: "
+ project.getName());
return null;
}
if (!model.isTypeChecked())
{
VdmTypeCheckerUi.typeCheck(HandlerUtil.getActiveShell(event), vdmProject);
}
if (!model.isTypeCorrect())
{
CodeGenConsole.GetInstance().println("Could not type check model: "
+ project.getName());
return null;
}
CodeGenConsole.GetInstance().println("Starting VDM to Java code generation...\n");
final List<String> classesToSkip = PluginVdm2JavaUtil.getClassesToSkip();
final JavaSettings javaSettings = getJavaSettings(project, classesToSkip);
final IRSettings irSettings = getIrSettings(project);
Job codeGenerate = new Job("VDM to Java code generation")
{
@Override
protected IStatus run(IProgressMonitor monitor)
{
if(javaSettings == null)
{
return Status.CANCEL_STATUS;
}
// Begin code generation
final JavaCodeGen vdm2java = new JavaCodeGen();
vdm2java.setSettings(irSettings);
vdm2java.setJavaSettings(javaSettings);
try
{
File eclipseProjectFolder = PluginVdm2JavaUtil.getEclipseProjectFolder(vdmProject);
// Clean folder with generated Java code
GeneralUtils.deleteFolderContents(eclipseProjectFolder, true);
// Generate user specified classes
GeneratedData generatedData = generateJava(vdmProject, model, vdm2java);
outputUserSpecifiedSkippedClasses(classesToSkip);
outputSkippedClasses(generatedData.getSkippedClasses());
File javaCodeOutputFolder = PluginVdm2JavaUtil.getJavaCodeOutputFolder(vdmProject, javaSettings);
try
{
vdm2java.genJavaSourceFiles(javaCodeOutputFolder, generatedData.getClasses());
CodeGenConsole.GetInstance().println("Project dialect: " + PluginVdm2JavaUtil.dialect2Str(vdmProject.getDialect()));
if(vdmProject.getDialect() == Dialect.VDM_RT)
{
CodeGenConsole.GetInstance().println("The current version of the Java code generator does not support the distributed aspects of the VDM-RT.");
CodeGenConsole.GetInstance().println("Ignoring deployment as well as cycles and duration statements...\n");
}
else
{
CodeGenConsole.GetInstance().println("");
}
} catch (Exception e)
{
CodeGenConsole.GetInstance().errorln("Problems saving the code generated Java source files to disk.");
CodeGenConsole.GetInstance().errorln("Try to run Overture with write permissions.\n");
if(SystemUtils.IS_OS_WINDOWS)
{
CodeGenConsole.GetInstance().println("Operating System: Windows.");
CodeGenConsole.GetInstance().println("If you installed Overture in a location such as \"C:\\Program Files\\Overture\"");
CodeGenConsole.GetInstance().println("you may need to give Overture permissions to write to the file system. You can try");
CodeGenConsole.GetInstance().println("run Overture as administrator and see if this solves the problem.");
}
return Status.CANCEL_STATUS;
}
File libFolder = PluginVdm2JavaUtil.getCodeGenRuntimeLibFolder(vdmProject);
try
{
PluginVdm2JavaUtil.copyCodeGenFile(PluginVdm2JavaUtil.CODEGEN_RUNTIME_BIN_FILE, libFolder);
outputRuntimeBinaries(libFolder);
}
catch(Exception e)
{
CodeGenConsole.GetInstance().errorln("Problems copying the Java code generator runtime library to " + libFolder.getAbsolutePath());
CodeGenConsole.GetInstance().errorln("Reason: " + e.getMessage());
}
try
{
PluginVdm2JavaUtil.copyCodeGenFile(PluginVdm2JavaUtil.CODEGEN_RUNTIME_SOURCES_FILE, libFolder);
outputRuntimeSources(libFolder);
}
catch(Exception e)
{
CodeGenConsole.GetInstance().errorln("Problems copying the Java code generator runtime library sources to " + libFolder.getAbsolutePath());
CodeGenConsole.GetInstance().errorln("Reason: " + e.getMessage());
}
if(generateJml(vdmProject))
{
try
{
PluginVdm2JavaUtil.copyCodeGenFile(PluginVdm2JavaUtil.VDM2JML_RUNTIME_BIN_FILE, libFolder);
outputVdm2JmlBinaries(libFolder);
}
catch(Exception e)
{
CodeGenConsole.GetInstance().errorln("Problems copying the VDM-to-JML runtime library to " + libFolder.getAbsolutePath());
CodeGenConsole.GetInstance().errorln("Reason: " + e.getMessage());
}
try
{
PluginVdm2JavaUtil.copyCodeGenFile(PluginVdm2JavaUtil.VDM2JML_RUNTIME_SOURCES_FILE, libFolder);
outputVdm2JmlSources(libFolder);
}
catch(Exception e)
{
CodeGenConsole.GetInstance().errorln("Problems copying the VDM-to-JML runtime library sources to " + libFolder.getAbsolutePath());
CodeGenConsole.GetInstance().errorln("Reason: " + e.getMessage());
}
}
try
{
PluginVdm2JavaUtil.copyCodeGenFile(PluginVdm2JavaUtil.ECLIPSE_RES_FILES_FOLDER + "/"
+ PluginVdm2JavaUtil.ECLIPSE_PROJECT_TEMPLATE_FILE, PluginVdm2JavaUtil.ECLIPSE_PROJECT_FILE, eclipseProjectFolder);
GeneralCodeGenUtils.replaceInFile(new File(eclipseProjectFolder, PluginVdm2JavaUtil.ECLIPSE_PROJECT_FILE), "%s", project.getName());
PluginVdm2JavaUtil.copyCodeGenFile(PluginVdm2JavaUtil.ECLIPSE_RES_FILES_FOLDER + "/"
+ PluginVdm2JavaUtil.ECLIPSE_CLASSPATH_TEMPLATE_FILE, PluginVdm2JavaUtil.ECLIPSE_CLASSPATH_FILE, eclipseProjectFolder);
// Always imports codegen-runtime.jar
String classPathEntries = PluginVdm2JavaUtil.RUNTIME_CLASSPATH_ENTRY;
if(generateJml(vdmProject))
{
// Import the VDM-to-JML runtime
classPathEntries += PluginVdm2JavaUtil.VDM2JML_CLASSPATH_ENTRY;
}
GeneralCodeGenUtils.replaceInFile(new File(eclipseProjectFolder, PluginVdm2JavaUtil.ECLIPSE_CLASSPATH_FILE), "%s", classPathEntries);
CodeGenConsole.GetInstance().println("Generated Eclipse project with Java generated code.\n");
} catch (Exception e)
{
e.printStackTrace();
CodeGenConsole.GetInstance().errorln("Problems generating the eclipse project with the generated Java code");
CodeGenConsole.GetInstance().errorln("Reason: "
+ e.getMessage());
}
outputUserspecifiedModules(javaCodeOutputFolder, generatedData.getClasses());
// Quotes generation
outputQuotes(vdmProject, javaCodeOutputFolder, vdm2java, generatedData.getQuoteValues());
// Renaming of variables shadowing other variables
outputRenamings(generatedData.getAllRenamings());
InvalidNamesResult invalidNames = generatedData.getInvalidNamesResult();
if (invalidNames != null && !invalidNames.isEmpty())
{
handleInvalidNames(invalidNames);
}
// Output any warnings such as problems with the user's launch configuration
outputWarnings(generatedData.getWarnings());
// Summarize the code generation process
int noOfClasses = generatedData.getClasses().size();
String msg = String.format("...finished Java code generation (generated %s %s).",
noOfClasses,
noOfClasses == 1 ? "class" : "classes");
CodeGenConsole.GetInstance().println(msg);
project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
} catch (AnalysisExceptionIR ex)
{
CodeGenConsole.GetInstance().println("Could not code generate VDM model: "
+ ex.getMessage());
} catch (Exception ex)
{
handleUnexpectedException(ex);
}
return Status.OK_STATUS;
}
};
codeGenerate.schedule();
return null;
}
public GeneratedData generateJava(final IVdmProject project,
final IVdmModel model, final JavaCodeGen vdm2java)
throws AnalysisException
{
if(project.getDialect() != Dialect.VDM_SL)
{
return vdm2java.generate(PluginVdm2JavaUtil.getNodes(model.getSourceUnits()));
}
else
{
if (generateJml(project))
{
JmlSettings jmlSettings = getJmlSettings();
JmlGenerator jmlGen = new JmlGenerator(vdm2java);
jmlGen.setJmlSettings(jmlSettings);
return jmlGen.generateJml(PluginVdm2JavaUtil.getModules(model.getSourceUnits()));
} else
{
return vdm2java.generate(PluginVdm2JavaUtil.getNodes(model.getSourceUnits()));
}
}
}
private boolean generateJml(IVdmProject project)
{
return project.getDialect() == Dialect.VDM_SL && getPrefs().getBoolean(ICodeGenConstants.GENERATE_JML, ICodeGenConstants.GENERATE_JML_DEFAULT);
}
private JmlSettings getJmlSettings()
{
Preferences preferences = getPrefs();
boolean useInvFor = preferences.getBoolean(ICodeGenConstants.JML_USE_INVARIANT_FOR, ICodeGenConstants.JML_USE_INVARIANT_FOR_DEFAULT);;
JmlSettings jmlSettings = new JmlSettings();
jmlSettings.setGenInvariantFor(useInvFor);
return jmlSettings;
}
public IRSettings getIrSettings(final IProject project)
{
Preferences preferences = getPrefs();
boolean generateCharSeqsAsStrings = preferences.getBoolean(ICodeGenConstants.GENERATE_CHAR_SEQUENCES_AS_STRINGS, ICodeGenConstants.GENERATE_CHAR_SEQUENCES_AS_STRING_DEFAULT);
boolean generateConcMechanisms = preferences.getBoolean(ICodeGenConstants.GENERATE_CONCURRENCY_MECHANISMS, ICodeGenConstants.GENERATE_CONCURRENCY_MECHANISMS_DEFAULT);
IRSettings irSettings = new IRSettings();
irSettings.setCharSeqAsString(generateCharSeqsAsStrings);
irSettings.setGenerateConc(generateConcMechanisms);
return irSettings;
}
private Preferences getPrefs()
{
Preferences preferences = InstanceScope.INSTANCE.getNode(ICodeGenConstants.PLUGIN_ID);
return preferences;
}
public JavaSettings getJavaSettings(final IProject project, List<String> classesToSkip)
{
Preferences preferences = getPrefs();
boolean disableCloning = preferences.getBoolean(ICodeGenConstants.DISABLE_CLONING, ICodeGenConstants.DISABLE_CLONING_DEFAULT);
String javaPackage = preferences.get(ICodeGenConstants.JAVA_PACKAGE, ICodeGenConstants.JAVA_PACKAGE_DEFAULT);
boolean genVdmLoc = preferences.getBoolean(ICodeGenConstants.GENERATE_VDM_LOCATIONS_INFO, ICodeGenConstants.GENERATE_VDM_LOCATIONS_INFO_DEFAULT);
JavaSettings javaSettings = new JavaSettings();
javaSettings.setDisableCloning(disableCloning);
javaSettings.setModulesToSkip(classesToSkip);
javaSettings.setJavaRootPackage(javaPackage);
javaSettings.setPrintVdmLocations(genVdmLoc);
if (!JavaCodeGenUtil.isValidJavaPackage(javaSettings.getJavaRootPackage()))
{
javaSettings.setJavaRootPackage(project.getName());
}
return javaSettings;
}
private void deleteMarkers(IProject project)
{
if (project == null)
{
return;
}
try
{
project.deleteMarkers(null, true, IResource.DEPTH_INFINITE);
} catch (CoreException ex)
{
Activator.log("Could not delete markers for project: "
+ project.toString(), ex);
ex.printStackTrace();
}
}
private void outputWarnings(List<String> warnings)
{
if(warnings != null && !warnings.isEmpty())
{
for(String warning : warnings)
{
CodeGenConsole.GetInstance().println(PluginVdm2JavaUtil.WARNING + " " + warning);
}
CodeGenConsole.GetInstance().errorln("");
}
}
private void outputUserSpecifiedSkippedClasses(
List<String> userspecifiedSkippedClasses)
{
if (!userspecifiedSkippedClasses.isEmpty())
{
CodeGenConsole.GetInstance().print("User specified filtered classes: ");
for (String skippedClass : userspecifiedSkippedClasses)
{
CodeGenConsole.GetInstance().print(skippedClass + " ");
}
CodeGenConsole.GetInstance().println("\n");
}
else
{
CodeGenConsole.GetInstance().println("No user specified classes to skip.\n");
}
}
private void outputSkippedClasses(List<String> skippedClasses)
{
if (!skippedClasses.isEmpty())
{
CodeGenConsole.GetInstance().print("Skipping classes (user specified and library named): ");
for (String skippedClass : skippedClasses)
{
CodeGenConsole.GetInstance().print(skippedClass + " ");
}
CodeGenConsole.GetInstance().println("\n");
}
}
private void outputRenamings(List<Renaming> allRenamings)
{
if(!allRenamings.isEmpty())
{
CodeGenConsole.GetInstance().println("Due to variable shadowing or normalisation of Java identifiers the following renamings of variables have been made:");
CodeGenConsole.GetInstance().println(GeneralCodeGenUtils.constructVarRenamingString(allRenamings));;
}
}
private void outputRuntimeBinaries(File outputFolder)
{
File runtime = new File(outputFolder, PluginVdm2JavaUtil.CODEGEN_RUNTIME_BIN_FILE);
CodeGenConsole.GetInstance().println("Copied the Java code generator runtime library to " + runtime.getAbsolutePath());
}
private void outputRuntimeSources(File outputFolder)
{
File runtime = new File(outputFolder, PluginVdm2JavaUtil.CODEGEN_RUNTIME_SOURCES_FILE);
CodeGenConsole.GetInstance().println("Copied the Java code generator runtime library sources to " + runtime.getAbsolutePath() + "\n");
}
private void outputVdm2JmlBinaries(File outputFolder)
{
File vdm2jmlRuntime = new File(outputFolder, PluginVdm2JavaUtil.VDM2JML_RUNTIME_BIN_FILE);
CodeGenConsole.GetInstance().println("Copied the VDM-to-JML runtime library to " + vdm2jmlRuntime.getAbsolutePath());
}
private void outputVdm2JmlSources(File outputFolder)
{
File vdm2jmlSources = new File(outputFolder, PluginVdm2JavaUtil.VDM2JML_RUNTIME_BIN_FILE);
CodeGenConsole.GetInstance().println("Copied the VDM-to-JML runtime library sources to " + vdm2jmlSources.getAbsolutePath() + "\n");
}
private void outputUserspecifiedModules(File outputFolder,
List<GeneratedModule> userspecifiedClasses)
{
for (GeneratedModule generatedModule : userspecifiedClasses)
{
if (generatedModule.hasMergeErrors())
{
CodeGenConsole.GetInstance().errorln(String.format("Could not generate Java for class %s. Following errors were found:", generatedModule.getName()));
List<Exception> mergeErrors = generatedModule.getMergeErrors();
for (Exception error : mergeErrors)
{
CodeGenConsole.GetInstance().errorln(error.toString());
}
} else if (!generatedModule.canBeGenerated())
{
CodeGenConsole.GetInstance().println("Could not code generate class: "
+ generatedModule.getName() + ".");
if(generatedModule.hasUnsupportedIrNodes())
{
LocationAssistantIR locationAssistant = assistantManager.getLocationAssistant();
List<VdmNodeInfo> unsupportedInIr = locationAssistant.getVdmNodeInfoLocationSorted(generatedModule.getUnsupportedInIr());
CodeGenConsole.GetInstance().println("Following VDM constructs are not supported by the code generator:");
for (VdmNodeInfo nodeInfo : unsupportedInIr)
{
String message = PluginVdm2JavaUtil.formatNodeString(nodeInfo, locationAssistant);
CodeGenConsole.GetInstance().println(message);
PluginVdm2JavaUtil.addMarkers(nodeInfo, locationAssistant);
}
}
if(generatedModule.hasUnsupportedTargLangNodes())
{
Set<IrNodeInfo> unsupportedInTargLang = generatedModule.getUnsupportedInTargLang();
CodeGenConsole.GetInstance().println("Following constructs are not supported by the code generator:");
for (IrNodeInfo nodeInfo : unsupportedInTargLang)
{
CodeGenConsole.GetInstance().println(nodeInfo.toString());
}
}
} else
{
File javaFile = new File(outputFolder, generatedModule.getName()
+ IJavaConstants.JAVA_FILE_EXTENSION);
CodeGenConsole.GetInstance().println("Generated class: "
+ generatedModule.getName());
CodeGenConsole.GetInstance().println("Java source file: "
+ javaFile.getAbsolutePath());
Set<IrNodeInfo> warnings = generatedModule.getTransformationWarnings();
if(!warnings.isEmpty())
{
CodeGenConsole.GetInstance().println("The following warnings were found for class " + generatedModule.getName() + ":");
for (IrNodeInfo nodeInfo : warnings)
{
CodeGenConsole.GetInstance().println(nodeInfo.getReason());
}
}
}
CodeGenConsole.GetInstance().println("");
}
}
private void outputQuotes(IVdmProject vdmProject, File outputFolder,
JavaCodeGen vdm2java, List<GeneratedModule> quotes) throws CoreException
{
if (quotes != null && !quotes.isEmpty())
{
for(GeneratedModule q : quotes)
{
vdm2java.genJavaSourceFile(outputFolder, q);
}
CodeGenConsole.GetInstance().println("Quotes generated to folder: "
+ outputFolder.getAbsolutePath());
CodeGenConsole.GetInstance().println("");
}
}
private void handleUnexpectedException(Exception ex)
{
String errorMessage =
"Unexpected problem encountered when attempting to code generate the VDM model.\n"
+ "The details of this problem have been reported in the Error Log.";
Activator.log(errorMessage, ex);
CodeGenConsole.GetInstance().errorln(errorMessage);
ex.printStackTrace();
}
private void handleInvalidNames(InvalidNamesResult invalidNames)
{
String message = "The model either uses words that are reserved by Java, declares VDM types"
+ " that uses Java type names or uses variable names that potentially"
+ " conflict with code generated temporary variable names";
CodeGenConsole.GetInstance().println("Warning: " + message);
String violationStr = GeneralCodeGenUtils.constructNameViolationsString(invalidNames);
CodeGenConsole.GetInstance().println(violationStr);
Set<Violation> typeNameViolations = invalidNames.getTypenameViolations();
PluginVdm2JavaUtil.addMarkers("Type name violation", typeNameViolations);
Set<Violation> reservedWordViolations = invalidNames.getReservedWordViolations();
PluginVdm2JavaUtil.addMarkers("Reserved word violations", reservedWordViolations);
}
}