org.overture.ide.plugins.codegen.commands.Vdm2JavaCommand.java Source code

Java tutorial

Introduction

Here is the source code for org.overture.ide.plugins.codegen.commands.Vdm2JavaCommand.java

Source

/*
 * #%~
 * 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.codegen.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.definitions.SClassDefinition;
import org.overture.codegen.analysis.vdm.Renaming;
import org.overture.codegen.analysis.violations.InvalidNamesResult;
import org.overture.codegen.analysis.violations.UnsupportedModelingException;
import org.overture.codegen.analysis.violations.Violation;
import org.overture.codegen.assistant.AssistantManager;
import org.overture.codegen.assistant.LocationAssistantCG;
import org.overture.codegen.ir.IRSettings;
import org.overture.codegen.ir.IrNodeInfo;
import org.overture.codegen.ir.VdmNodeInfo;
import org.overture.codegen.utils.AnalysisExceptionCG;
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.IJavaCodeGenConstants;
import org.overture.codegen.vdm2java.JavaCodeGen;
import org.overture.codegen.vdm2java.JavaCodeGenUtil;
import org.overture.codegen.vdm2java.JavaSettings;
import org.overture.config.Settings;
import org.overture.ide.core.IVdmModel;
import org.overture.ide.core.resources.IVdmProject;
import org.overture.ide.core.resources.IVdmSourceUnit;
import org.overture.ide.plugins.codegen.Activator;
import org.overture.ide.plugins.codegen.CodeGenConsole;
import org.overture.ide.plugins.codegen.ICodeGenConstants;
import org.overture.ide.plugins.codegen.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();

        deleteMarkers(project);

        final IVdmModel model = vdmProject.getModel();

        if (!PluginVdm2JavaUtil.isSupportedVdmDialect(vdmProject)) {
            CodeGenConsole.GetInstance().println("Project : " + project.getName()
                    + " is not supported by the Java code generator. Currently, VDM++ is the only supported dialect.");
            return null;
        }

        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;
        }

        Job codeGenerate = new Job("Code generate") {
            @Override
            protected IStatus run(IProgressMonitor monitor) {
                // Begin code generation
                final JavaCodeGen vdm2java = new JavaCodeGen();

                Preferences preferences = InstanceScope.INSTANCE.getNode(ICodeGenConstants.PLUGIN_ID);

                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);

                boolean disableCloning = preferences.getBoolean(ICodeGenConstants.DISABLE_CLONING,
                        ICodeGenConstants.DISABLE_CLONING_DEFAULT);

                JavaSettings javaSettings = new JavaSettings();
                javaSettings.setDisableCloning(disableCloning);
                List<String> classesToSkip = PluginVdm2JavaUtil.getClassesToSkip();
                javaSettings.setClassesToSkip(classesToSkip);

                vdm2java.setSettings(irSettings);
                vdm2java.setJavaSettings(javaSettings);

                try {
                    CodeGenConsole.GetInstance().clearConsole();
                    CodeGenConsole.GetInstance().println("Starting VDM++ to Java code generation...\n");

                    File outputFolder = PluginVdm2JavaUtil.getOutputFolder(vdmProject);

                    // Clean folder with generated Java code
                    GeneralUtils.deleteFolderContents(outputFolder);

                    // Generate user specified classes
                    List<IVdmSourceUnit> sources = model.getSourceUnits();
                    List<SClassDefinition> mergedParseLists = PluginVdm2JavaUtil.mergeParseLists(sources);
                    GeneratedData generatedData = vdm2java.generateJavaFromVdm(mergedParseLists);

                    outputUserSpecifiedSkippedClasses(classesToSkip);
                    outputSkippedClasses(generatedData.getSkippedClasses());

                    File javaOutputFolder = new File(outputFolder,
                            PluginVdm2JavaUtil.CODEGEN_RUNTIME_SRC_FOLDER_NAME);

                    try {
                        vdm2java.generateJavaSourceFiles(javaOutputFolder, generatedData.getClasses());
                    } catch (Exception e) {
                        CodeGenConsole.GetInstance()
                                .printErrorln("Problems saving the code generated Java source files to disk.");
                        CodeGenConsole.GetInstance().printErrorln("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 = new File(outputFolder, PluginVdm2JavaUtil.CODEGEN_RUNTIME_LIB_FOLDER_NAME);
                    try {
                        PluginVdm2JavaUtil.copyCodeGenFile(PluginVdm2JavaUtil.CODEGEN_RUNTIME_BIN_FILE_NAME,
                                libFolder);
                        outputRuntimeBinaries(libFolder);
                    } catch (Exception e) {
                        CodeGenConsole.GetInstance()
                                .printErrorln("Problems copying the Java code generator runtime library to "
                                        + outputFolder.getAbsolutePath());
                        CodeGenConsole.GetInstance().printErrorln("Reason: " + e.getMessage());
                    }

                    try {
                        PluginVdm2JavaUtil.copyCodeGenFile(PluginVdm2JavaUtil.CODEGEN_RUNTIME_SOURCES_FILE_NAME,
                                libFolder);
                        outputRuntimeSources(libFolder);
                    } catch (Exception e) {
                        CodeGenConsole.GetInstance()
                                .printErrorln("Problems copying the Java code generator runtime library sources to "
                                        + outputFolder.getAbsolutePath());
                        CodeGenConsole.GetInstance().printErrorln("Reason: " + e.getMessage());
                    }

                    try {
                        PluginVdm2JavaUtil.copyCodeGenFile(
                                PluginVdm2JavaUtil.ECLIPSE_RES_FILES_FOLDER_NAME + "/"
                                        + PluginVdm2JavaUtil.ECLIPSE_PROJECT_TEMPLATE_FILE_NAME,
                                PluginVdm2JavaUtil.ECLIPSE_PROJECT_FILE_NAME, outputFolder);
                        PluginVdm2JavaUtil.copyCodeGenFile(
                                PluginVdm2JavaUtil.ECLIPSE_RES_FILES_FOLDER_NAME + "/"
                                        + PluginVdm2JavaUtil.ECLIPSE_CLASSPATH_TEMPLATE_FILE_NAME,
                                PluginVdm2JavaUtil.ECLIPSE_CLASSPATH_FILE_NAME, outputFolder);

                        GeneralCodeGenUtils.replaceInFile(
                                new File(outputFolder, PluginVdm2JavaUtil.ECLIPSE_PROJECT_FILE_NAME), "%s",
                                project.getName());

                        CodeGenConsole.GetInstance()
                                .println("Generated Eclipse project with Java generated code.\n");

                    } catch (Exception e) {
                        e.printStackTrace();
                        CodeGenConsole.GetInstance().printErrorln(
                                "Problems generating the eclipse project with the generated Java code");
                        CodeGenConsole.GetInstance().printErrorln("Reason: " + e.getMessage());
                    }

                    outputUserspecifiedModules(javaOutputFolder, generatedData.getClasses());

                    // Quotes generation
                    outputQuotes(vdmProject, new File(javaOutputFolder, PluginVdm2JavaUtil.QUOTES_FOLDER), vdm2java,
                            generatedData.getQuoteValues());

                    // Renaming of variables shadowing other variables
                    outputRenamings(generatedData.getAllRenamings());

                    InvalidNamesResult invalidNames = generatedData.getInvalidNamesResult();

                    if (invalidNames != null && !invalidNames.isEmpty()) {
                        handleInvalidNames(invalidNames);
                    }

                    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 (UnsupportedModelingException ex) {
                    handleUnsupportedModeling(ex);
                } catch (AnalysisExceptionCG 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;
    }

    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 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("Hidden variables found! Following variable renamings were done: ");
            CodeGenConsole.GetInstance().println(JavaCodeGenUtil.constructVarRenamingString(allRenamings));
            ;
        }
    }

    private void outputRuntimeBinaries(File outputFolder) {
        File runtime = new File(outputFolder, PluginVdm2JavaUtil.CODEGEN_RUNTIME_BIN_FILE_NAME);
        CodeGenConsole.GetInstance()
                .println("Copied the Java code generator runtime library to " + runtime.getAbsolutePath() + "\n");
    }

    private void outputRuntimeSources(File outputFolder) {
        File runtime = new File(outputFolder, PluginVdm2JavaUtil.CODEGEN_RUNTIME_SOURCES_FILE_NAME);
        CodeGenConsole.GetInstance().println(
                "Copied the Java code generator runtime library sources to " + runtime.getAbsolutePath() + "\n");
    }

    private void outputUserspecifiedModules(File outputFolder, List<GeneratedModule> userspecifiedClasses) {
        for (GeneratedModule generatedModule : userspecifiedClasses) {
            if (generatedModule.hasMergeErrors()) {
                CodeGenConsole.GetInstance().printErrorln(
                        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().printErrorln(error.toString());
                }
            } else if (!generatedModule.canBeGenerated()) {
                CodeGenConsole.GetInstance()
                        .println("Could not code generate class: " + generatedModule.getName() + ".");

                if (generatedModule.hasUnsupportedIrNodes()) {
                    LocationAssistantCG 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() + IJavaCodeGenConstants.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.generateJavaSourceFile(outputFolder, q);
            }

            CodeGenConsole.GetInstance().println("Quotes generated to folder: " + outputFolder.getAbsolutePath());
            CodeGenConsole.GetInstance().println("");
        }
    }

    private void handleUnexpectedException(Exception ex) {
        String errorMessage = "Unexpected exception caught when attempting to code generate VDM model.";

        Activator.log(errorMessage, ex);

        CodeGenConsole.GetInstance().println(errorMessage);
        CodeGenConsole.GetInstance().println(ex.getMessage());
        ex.printStackTrace();
    }

    private void handleUnsupportedModeling(UnsupportedModelingException ex) {
        CodeGenConsole.GetInstance().println("Could not code generate VDM model: " + ex.getMessage());

        String violationStr = JavaCodeGenUtil.constructUnsupportedModelingString(ex);
        CodeGenConsole.GetInstance().println(violationStr);

        Set<Violation> violations = ex.getViolations();
        PluginVdm2JavaUtil.addMarkers("Modeling rule not supported", violations);
    }

    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 = JavaCodeGenUtil.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);
    }
}