/*
* #%~
* VDM Code Generator
* %%
* 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.codegen.vdm2java;
import java.io.File;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.definitions.SClassDefinition;
import org.overture.ast.lex.Dialect;
import org.overture.ast.modules.AModuleModules;
import org.overture.codegen.analysis.vdm.Renaming;
import org.overture.codegen.analysis.violations.InvalidNamesResult;
import org.overture.codegen.ir.CodeGenBase;
import org.overture.codegen.ir.IRConstants;
import org.overture.codegen.ir.IRSettings;
import org.overture.codegen.ir.IrNodeInfo;
import org.overture.codegen.printer.MsgPrinter;
import org.overture.codegen.utils.GeneralCodeGenUtils;
import org.overture.codegen.utils.GeneralUtils;
import org.overture.codegen.utils.Generated;
import org.overture.codegen.utils.GeneratedData;
import org.overture.codegen.utils.GeneratedModule;
import org.overture.config.Release;
import org.overture.config.Settings;
import org.overture.typechecker.util.TypeCheckerUtil;
import org.overture.typechecker.util.TypeCheckerUtil.TypeCheckResult;
public class JavaCodeGenMain
{
// Command-line args
public static final String OO_ARG = "-pp";
public static final String RT_ARG = "-rt";
public static final String SL_ARG = "-sl";
public static final String CLASSIC = "-classic";
public static final String VDM10 = "-vdm10";
public static final String EXP_ARG = "-exp";
public static final String FOLDER_ARG = "-folder";
public static final String PRINT_ARG = "-print";
public static final String PACKAGE_ARG = "-package";
public static final String OUTPUT_ARG = "-output";
public static final String VDM_ENTRY_EXP = "-entry";
public static final String NO_CODE_FORMAT = "-nocodeformat";
public static final String JUNIT4 = "-junit4";
public static final String SEP_TEST_CODE = "-separate";
public static final String VDM_LOC = "-vdmloc";
public static final String NO_CLONING = "-nocloning";
public static final String NO_STRINGS = "-nostrings";
// Folder names
private static final String GEN_MODEL_CODE_FOLDER = "main";
private static final String GEN_TESTS_FOLDER = "test";
public static void main(String[] args)
{
Settings.release = Release.VDM_10;
JavaCodeGenMode cgMode = null;
boolean printClasses = false;
if (args == null || args.length <= 1)
{
usage("Too few arguments provided");
}
IRSettings irSettings = new IRSettings();
irSettings.setCharSeqAsString(true);
irSettings.setGeneratePreConds(false);
irSettings.setGeneratePreCondChecks(false);
irSettings.setGeneratePostConds(false);
irSettings.setGeneratePostCondChecks(false);
JavaSettings javaSettings = new JavaSettings();
javaSettings.setDisableCloning(false);
List<String> listArgs = Arrays.asList(args);
String exp = null;
File outputDir = null;
List<File> files = new LinkedList<File>();
Settings.release = Release.VDM_10;
boolean separateTestCode = false;
for (Iterator<String> i = listArgs.iterator(); i.hasNext();)
{
String arg = i.next();
if (arg.equals(OO_ARG))
{
cgMode = JavaCodeGenMode.OO_SPEC;
Settings.dialect = Dialect.VDM_PP;
} else if (arg.equals(RT_ARG))
{
cgMode = JavaCodeGenMode.OO_SPEC;
Settings.dialect = Dialect.VDM_RT;
} else if (arg.equals(SL_ARG))
{
cgMode = JavaCodeGenMode.SL_SPEC;
Settings.dialect = Dialect.VDM_SL;
} else if (arg.equals(CLASSIC))
{
Settings.release = Release.CLASSIC;
} else if (arg.equals(VDM10))
{
Settings.release = Release.VDM_10;
} else if (arg.equals(EXP_ARG))
{
cgMode = JavaCodeGenMode.EXP;
if (i.hasNext())
{
exp = i.next();
} else
{
usage(EXP_ARG + " requires a VDM expression");
}
} else if (arg.equals(PRINT_ARG))
{
printClasses = true;
} else if (arg.equals(FOLDER_ARG))
{
if (i.hasNext())
{
File path = new File(i.next());
if (path.isDirectory())
{
files.addAll(filterFiles(GeneralUtils.getFiles(path)));
} else
{
usage("Could not find path: " + path);
}
} else
{
usage(FOLDER_ARG + " requires a directory");
}
} else if (arg.equals(PACKAGE_ARG))
{
if (i.hasNext())
{
String javaPackage = i.next();
if (JavaCodeGenUtil.isValidJavaPackage(javaPackage))
{
javaSettings.setJavaRootPackage(javaPackage);
} else
{
MsgPrinter.getPrinter().errorln("Not a valid java package. Using the default java package instead..\n");
}
}
} else if (arg.equals(OUTPUT_ARG))
{
if (i.hasNext())
{
outputDir = new File(i.next());
outputDir.mkdirs();
if (!outputDir.isDirectory())
{
usage(outputDir + " is not a directory");
}
} else
{
usage(OUTPUT_ARG + " requires a directory");
}
} else if (arg.equals(VDM_ENTRY_EXP))
{
if (i.hasNext())
{
javaSettings.setVdmEntryExp(i.next());
}
} else if (arg.equals(NO_CODE_FORMAT))
{
javaSettings.setFormatCode(false);
} else if (arg.equals(JUNIT4))
{
javaSettings.setGenJUnit4tests(true);
} else if (arg.equals(SEP_TEST_CODE))
{
separateTestCode = true;
} else if (arg.equals(VDM_LOC))
{
javaSettings.setPrintVdmLocations(true);
} else if (arg.equals(NO_CLONING))
{
javaSettings.setDisableCloning(true);
} else if(arg.equals(NO_STRINGS))
{
irSettings.setCharSeqAsString(false);
}
else
{
// It's a file or a directory
File file = new File(arg);
if (file.isFile())
{
if (GeneralCodeGenUtils.isVdmSourceFile(file))
{
files.add(file);
}
} else
{
usage("Not a file: " + file);
}
}
}
if (Settings.dialect == null)
{
usage("No VDM dialect specified");
}
MsgPrinter.getPrinter().println("Starting code generation...\n");
if (cgMode == JavaCodeGenMode.EXP)
{
handleExp(exp, irSettings, javaSettings, Settings.dialect);
} else
{
if (files.isEmpty())
{
usage("Input files are missing");
}
if (outputDir == null && !printClasses)
{
MsgPrinter.getPrinter().println("No output directory specified - printing code generated classes instead..\n");
printClasses = true;
}
if (cgMode == JavaCodeGenMode.OO_SPEC)
{
handleOo(files, irSettings, javaSettings, Settings.dialect, printClasses, outputDir, separateTestCode);
} else if (cgMode == JavaCodeGenMode.SL_SPEC)
{
handleSl(files, irSettings, javaSettings, printClasses, outputDir, separateTestCode);
} else
{
MsgPrinter.getPrinter().errorln("Unexpected dialect: "
+ cgMode);
}
}
MsgPrinter.getPrinter().println("\nFinished code generation! Bye...");
}
private static void handleExp(String exp, IRSettings irSettings,
JavaSettings javaSettings, Dialect dialect)
{
try
{
Settings.release = Release.VDM_10;
Settings.dialect = Dialect.VDM_PP;
Generated generated = JavaCodeGenUtil.generateJavaFromExp(exp, irSettings, javaSettings, dialect);
if (generated.hasMergeErrors())
{
MsgPrinter.getPrinter().println(String.format("VDM expression '%s' could not be merged. Following merge errors were found:", exp));
GeneralCodeGenUtils.printMergeErrors(generated.getMergeErrors());
} else if (!generated.canBeGenerated())
{
MsgPrinter.getPrinter().println("Could not generate VDM expression: "
+ exp);
if (generated.hasUnsupportedIrNodes())
{
GeneralCodeGenUtils.printUnsupportedIrNodes(generated.getUnsupportedInIr());
}
if (generated.hasUnsupportedTargLangNodes())
{
GeneralCodeGenUtils.printUnsupportedNodes(generated.getUnsupportedInTargLang());
}
} else
{
MsgPrinter.getPrinter().println("Code generated expression: "
+ generated.getContent().trim());
}
} catch (AnalysisException e)
{
MsgPrinter.getPrinter().println("Could not code generate model: "
+ e.getMessage());
}
}
public static void handleSl(List<File> files, IRSettings irSettings,
JavaSettings javaSettings, boolean printCode, File outputDir,
boolean separateTestCode)
{
try
{
JavaCodeGen vdmCodGen = new JavaCodeGen();
vdmCodGen.setSettings(irSettings);
vdmCodGen.setJavaSettings(javaSettings);
Settings.dialect = Dialect.VDM_SL;
TypeCheckResult<List<AModuleModules>> tcResult = TypeCheckerUtil.typeCheckSl(files);
if (GeneralCodeGenUtils.hasErrors(tcResult))
{
MsgPrinter.getPrinter().error("Found errors in VDM model:");
MsgPrinter.getPrinter().errorln(GeneralCodeGenUtils.errorStr(tcResult));
return;
}
GeneratedData data = vdmCodGen.generate(CodeGenBase.getNodes(tcResult.result));
processData(printCode, outputDir, vdmCodGen, data, separateTestCode);
} catch (AnalysisException e)
{
MsgPrinter.getPrinter().println("Could not code generate model: "
+ e.getMessage());
}
}
public static void handleOo(List<File> files, IRSettings irSettings,
JavaSettings javaSettings, Dialect dialect, boolean printCode,
File outputDir, boolean separateTestCode)
{
try
{
JavaCodeGen vdmCodGen = new JavaCodeGen();
vdmCodGen.setSettings(irSettings);
vdmCodGen.setJavaSettings(javaSettings);
TypeCheckResult<List<SClassDefinition>> tcResult = null;
if (dialect == Dialect.VDM_PP)
{
tcResult = TypeCheckerUtil.typeCheckPp(files);
} else
{
tcResult = TypeCheckerUtil.typeCheckRt(files);
}
if (GeneralCodeGenUtils.hasErrors(tcResult))
{
MsgPrinter.getPrinter().error("Found errors in VDM model:");
MsgPrinter.getPrinter().errorln(GeneralCodeGenUtils.errorStr(tcResult));
return;
}
GeneratedData data = vdmCodGen.generate(CodeGenBase.getNodes(tcResult.result));
processData(printCode, outputDir, vdmCodGen, data, separateTestCode);
} catch (AnalysisException e)
{
MsgPrinter.getPrinter().println("Could not code generate model: "
+ e.getMessage());
}
}
public static void processData(boolean printCode, final File outputDir,
JavaCodeGen vdmCodGen, GeneratedData data, boolean separateTestCode)
{
List<GeneratedModule> generatedClasses = data.getClasses();
MsgPrinter.getPrinter().println("");
if (!generatedClasses.isEmpty())
{
for (GeneratedModule generatedClass : generatedClasses)
{
if (generatedClass.hasMergeErrors())
{
MsgPrinter.getPrinter().println(String.format("Class %s could not be merged. Following merge errors were found:", generatedClass.getName()));
GeneralCodeGenUtils.printMergeErrors(generatedClass.getMergeErrors());
} else if (!generatedClass.canBeGenerated())
{
MsgPrinter.getPrinter().println("Could not generate class: "
+ generatedClass.getName() + "\n");
if (generatedClass.hasUnsupportedIrNodes())
{
MsgPrinter.getPrinter().println("Following VDM constructs are not supported by the code generator:");
GeneralCodeGenUtils.printUnsupportedIrNodes(generatedClass.getUnsupportedInIr());
}
if (generatedClass.hasUnsupportedTargLangNodes())
{
MsgPrinter.getPrinter().println("Following constructs are not supported by the code generator:");
GeneralCodeGenUtils.printUnsupportedNodes(generatedClass.getUnsupportedInTargLang());
}
} else
{
if (outputDir != null)
{
if (separateTestCode)
{
if (generatedClass.isTestCase())
{
vdmCodGen.genJavaSourceFile(new File(outputDir, GEN_TESTS_FOLDER), generatedClass);
} else
{
vdmCodGen.genJavaSourceFile(new File(outputDir, GEN_MODEL_CODE_FOLDER), generatedClass);
}
} else
{
vdmCodGen.genJavaSourceFile(outputDir, generatedClass);
}
}
if (printCode)
{
MsgPrinter.getPrinter().println("**********");
MsgPrinter.getPrinter().println(generatedClass.getContent());
MsgPrinter.getPrinter().println("\n");
} else
{
MsgPrinter.getPrinter().println("Generated class : "
+ generatedClass.getName());
}
Set<IrNodeInfo> warnings = generatedClass.getTransformationWarnings();
if (!warnings.isEmpty())
{
MsgPrinter.getPrinter().println("Following transformation warnings were found:");
GeneralCodeGenUtils.printUnsupportedNodes(generatedClass.getTransformationWarnings());
}
}
}
} else
{
MsgPrinter.getPrinter().println("No classes were generated!");
}
List<GeneratedModule> quotes = data.getQuoteValues();
MsgPrinter.getPrinter().println("\nGenerated following quotes:");
if (quotes != null && !quotes.isEmpty())
{
if (outputDir != null)
{
for (GeneratedModule q : quotes)
{
if (separateTestCode)
{
vdmCodGen.genJavaSourceFile(new File(outputDir, GEN_MODEL_CODE_FOLDER), q);
} else
{
vdmCodGen.genJavaSourceFile(outputDir, q);
}
}
}
for (GeneratedModule q : quotes)
{
MsgPrinter.getPrinter().print(q.getName() + " ");
}
MsgPrinter.getPrinter().println("");
}
InvalidNamesResult invalidName = data.getInvalidNamesResult();
if (!invalidName.isEmpty())
{
MsgPrinter.getPrinter().println(GeneralCodeGenUtils.constructNameViolationsString(invalidName));
}
List<Renaming> allRenamings = data.getAllRenamings();
if (!allRenamings.isEmpty())
{
MsgPrinter.getPrinter().println("\nDue to variable shadowing or normalisation of Java identifiers the following renamings of variables have been made: ");
MsgPrinter.getPrinter().println(GeneralCodeGenUtils.constructVarRenamingString(allRenamings));
}
if (data.getWarnings() != null && !data.getWarnings().isEmpty())
{
MsgPrinter.getPrinter().println("");
for (String w : data.getWarnings())
{
MsgPrinter.getPrinter().println("[WARNING] " + w);
}
}
}
public static List<File> filterFiles(List<File> files)
{
List<File> filtered = new LinkedList<File>();
for (File f : files)
{
if (GeneralCodeGenUtils.isVdmSourceFile(f))
{
filtered.add(f);
}
}
return filtered;
}
public static void usage(String msg)
{
MsgPrinter.getPrinter().errorln("VDM-to-Java Code Generator: " + msg
+ "\n");
MsgPrinter.getPrinter().errorln("Usage: CodeGen < " + OO_ARG + " | "
+ SL_ARG + " | " + RT_ARG + " | -exp > [<options>] [<files>]");
MsgPrinter.getPrinter().errorln(OO_ARG
+ ": code generate a VDMPP specification consisting of multiple .vdmpp files");
MsgPrinter.getPrinter().errorln(SL_ARG
+ ": code generate a VDMSL specification consisting of multiple .vdmsl files");
MsgPrinter.getPrinter().errorln(RT_ARG
+ ": code generate a limited part of a VDMRT specification consisting of multiple .vdmrt files");
MsgPrinter.getPrinter().errorln(CLASSIC
+ ": code generate using the VDM classic language release");
MsgPrinter.getPrinter().errorln(VDM10
+ ": code generate using the VDM-10 language release");
MsgPrinter.getPrinter().errorln(EXP_ARG
+ " <expression>: code generate a VDMPP expression");
MsgPrinter.getPrinter().errorln(FOLDER_ARG
+ " <folder path>: a folder containing input vdm source files");
MsgPrinter.getPrinter().errorln(PRINT_ARG
+ ": print the generated code to the console");
MsgPrinter.getPrinter().errorln(PACKAGE_ARG
+ " <java package>: the output java package of the generated code (e.g. my.code)");
MsgPrinter.getPrinter().errorln(OUTPUT_ARG
+ " <folder path>: the output folder of the generated code");
MsgPrinter.getPrinter().errorln(VDM_ENTRY_EXP
+ " <vdm entry point expression>: generate a Java main method based on the specified entry point");
MsgPrinter.getPrinter().errorln(NO_CODE_FORMAT
+ ": to NOT format the generated Java code");
MsgPrinter.getPrinter().errorln(JUNIT4 + ": to generate VDMUnit "
+ IRConstants.TEST_CASE + " sub-classes to JUnit4 tests");
MsgPrinter.getPrinter().errorln(SEP_TEST_CODE
+ ": to place the code generated model and the test code into separate folders named '"
+ GEN_MODEL_CODE_FOLDER + "' and '" + GEN_TESTS_FOLDER
+ "', respectively");
MsgPrinter.getPrinter().errorln(VDM_LOC
+ ": Generate VDM location information for code generated constructs");
MsgPrinter.getPrinter().errorln(NO_CLONING
+ ": To disable deep cloning of value types");
// Terminate
System.exit(1);
}
}