import java_cup.runtime.ComplexSymbolFactory;
import java_cup.runtime.ScannerBuffer;
import java.io.*;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import iotpolicy.tree.InterfaceDecl;
import iotpolicy.tree.RequiresDecl;
-/** Class IoTCompiler is the main stub compiler for
- * stub files generation. This class calls helper classes
+/** Class IoTCompiler is the main interface/stub compiler for
+ * files generation. This class calls helper classes
* such as Parser, Lexer, InterfaceDecl, CapabilityDecl,
* RequiresDecl, ParseTreeHandler, etc.
*
private CapabilityDecl capDecl;
private RequiresDecl reqDecl;
private Map<String,Set<String>> mapCapabMethods;
+ // Data structure to store our types (primitives and non-primitives) for compilation
+ //private Set<String> setPrimitives;
+ private Map<String,String> mapPrimitives;
+ private Map<String,String> mapNonPrimitivesJava;
+ private Map<String,String> mapNonPrimitivesCplus;
private PrintWriter pw;
private String dir;
+ private String subdir;
/**
* Class constants
*/
- private final static String OUTPUT_DIRECTORY = "stubfiles";
+ private final static String OUTPUT_DIRECTORY = "output_files";
+
+ /**
+ * Primitive data types
+ */
+ private final static String[] primitives = new String[] {
+
+ "byte",
+ "Byte",
+ "short",
+ "Short",
+ "int",
+ "Integer",
+ "long",
+ "Long",
+ "float",
+ "Float",
+ "double",
+ "Double",
+ "boolean",
+ "Boolean",
+ "char",
+ "Character",
+ "string",
+ "String",
+ "void"
+ };
+
+ /**
+ * Primitive data types in C++ to map the primitives list
+ */
+ private final static String[] primitivesCplus = new String[] {
+
+ "char",
+ "char",
+ "short",
+ "short",
+ "int",
+ "int",
+ "long",
+ "long",
+ "float",
+ "float",
+ "double",
+ "double",
+ "bool",
+ "bool",
+ "char",
+ "char",
+ "string",
+ "string",
+ "void"
+ };
+
+ /**
+ * Non-primitive data types supported by this compiler
+ */
+ private final static String[] nonPrimitives = new String[] {
+
+ "Set",
+ "HashSet",
+ "Map",
+ "HashMap",
+ "List",
+ "ArrayList"
+ };
+
+ /**
+ * Non-primitive Java libraries based on the list above
+ */
+ private final static String[] nonPrimitiveJavaLibs = new String[] {
+
+ "java.util.Set",
+ "java.util.HashSet",
+ "java.util.Map",
+ "java.util.HashMap",
+ "java.util.List",
+ "java.util.ArrayList"
+ };
+
+ /**
+ * Non-primitive C++ libraries based on the list above
+ */
+ private final static String[] nonPrimitiveCplusLibs = new String[] {
+
+ "set",
+ "unordered_set",
+ "map",
+ "unordered_map",
+ "list",
+ "list"
+ };
+
+ private enum ParamCategory {
+
+ PRIMITIVES, // All the primitive types, e.g. byte, short, int, long, etc.
+ NONPRIMITIVES, // Non-primitive types, e.g. Set, Map, List, etc.
+ USERDEFINED // Non-supported type by default; assumed as driver classes
+ }
/**
* Class constructors
capDecl = null;
capDecl = null;
mapCapabMethods = new HashMap<String,Set<String>>();
+ mapPrimitives = new HashMap<String,String>();
+ arraysToMap(mapPrimitives, primitives, primitivesCplus);
+ mapNonPrimitivesJava = new HashMap<String,String>();
+ arraysToMap(mapNonPrimitivesJava, nonPrimitives, nonPrimitiveJavaLibs);
+ mapNonPrimitivesCplus = new HashMap<String,String>();
+ arraysToMap(mapNonPrimitivesCplus, nonPrimitives, nonPrimitiveCplusLibs);
pw = null;
dir = OUTPUT_DIRECTORY;
+ subdir = null;
}
capDecl = null;
reqDecl = null;
mapCapabMethods = new HashMap<String,Set<String>>();
+ mapPrimitives = new HashMap<String,String>();
+ arraysToMap(mapPrimitives, primitives, primitivesCplus);
+ mapNonPrimitivesJava = new HashMap<String,String>();
+ arraysToMap(mapNonPrimitivesJava, nonPrimitives, nonPrimitiveJavaLibs);
+ mapNonPrimitivesCplus = new HashMap<String,String>();
+ arraysToMap(mapNonPrimitivesCplus, nonPrimitives, nonPrimitiveCplusLibs);
pw = null;
dir = OUTPUT_DIRECTORY;
+ subdir = null;
}
}
+ /**
+ * generateJavaLocalInterface() writes the local interface to provide type-checking
+ * <p>
+ * It needs to rewrite and exchange USERDEFINED types in input parameters of stub
+ * and original interfaces, e.g. exchange Camera and CameraWithVideoAndRecording.
+ * The local interface has to be the input parameter for the stub and the stub
+ * interface has to be the input parameter for the local class.
+ */
+ public void generateJavaLocalInterface() throws IOException {
+
+ // Create a new directory
+ createDirectory(dir);
+ // Open a new file to write into
+ String intface = origInt;
+ FileWriter fw = new FileWriter(dir + "/" + intface + ".java");
+ pw = new PrintWriter(new BufferedWriter(fw));
+ // Pass in set of methods and get import classes
+ List<String> methods = intDecl.getMethods();
+ Set<String> importClasses = getImportClasses(methods);
+ printImportStatements(importClasses);
+ // Write interface header
+ println("");
+ println("public interface " + intface + " {");
+ // Write methods
+ for (String method : methods) {
+
+ List<String> methParams = intDecl.getMethodParams(method);
+ List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
+ print("public " + intDecl.getMethodType(method) + " " +
+ intDecl.getMethodId(method) + "(");
+ for (int i = 0; i < methParams.size(); i++) {
+ // Check for params with driver class types and exchange it
+ // with its remote interface
+ String paramType = checkAndGetParamClass(methPrmTypes.get(i));
+ print(paramType + " " + methParams.get(i));
+ // Check if this is the last element (don't print a comma)
+ if (i != methParams.size() - 1) {
+ print(", ");
+ }
+ }
+ println(");");
+ }
+ println("}");
+ pw.close();
+ System.out.println("IoTCompiler: Generated local interface " + intface + ".java...");
+ }
+
+
+ /**
+ * generateCplusLocalInterface() writes the local interface to provide type-checking
+ * <p>
+ * It needs to rewrite and exchange USERDEFINED types in input parameters of stub
+ * and original interfaces, e.g. exchange Camera and CameraWithVideoAndRecording.
+ * The local interface has to be the input parameter for the stub and the stub
+ * interface has to be the input parameter for the local class.
+ */
+ public void generateCplusLocalInterface() throws IOException {
+
+ // Create a new directory
+ createDirectory(dir);
+ // Open a new file to write into
+ String intface = origInt;
+ FileWriter fw = new FileWriter(dir + "/" + intface + ".hpp");
+ pw = new PrintWriter(new BufferedWriter(fw));
+ // Write file headers
+ println("#include <iostream>");
+ // Pass in set of methods and get import classes
+ List<String> methods = intDecl.getMethods();
+ Set<String> includeClasses = getIncludeClasses(methods);
+ printIncludeStatements(includeClasses);
+ println("");
+ println("using namespace std;");
+ println("");
+ println("class " + intface);
+ println("{");
+ println("public:");
+ // Write methods
+ for (String method : methods) {
+
+ List<String> methParams = intDecl.getMethodParams(method);
+ List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
+ print("virtual " + convertType(intDecl.getMethodType(method)) + " " +
+ intDecl.getMethodId(method) + "(");
+ for (int i = 0; i < methParams.size(); i++) {
+ // Check for params with driver class types and exchange it
+ // with its remote interface
+ String paramType = checkAndGetParamClass(methPrmTypes.get(i));
+ paramType = checkAndGetCplusType(paramType);
+ print(paramType + " " + methParams.get(i));
+ // Check if this is the last element (don't print a comma)
+ if (i != methParams.size() - 1) {
+ print(", ");
+ }
+ }
+ println(") = 0;");
+ }
+ print("}");
+ println(";");
+ pw.close();
+ System.out.println("IoTCompiler: Generated local interface " + intface + ".hpp...");
+ }
+
+
/**
* generateJavaInterfaces() generate stub interfaces based on the methods list in Java
*/
public void generateJavaInterfaces() throws IOException {
// Create a new directory
- createDirectory(dir);
+ createDirectories(dir, subdir);
for (Map.Entry<String,Set<String>> intMeth : mapCapabMethods.entrySet()) {
// Open a new file to write into
String newIntface = intMeth.getKey();
- FileWriter fw = new FileWriter(dir + "/" + newIntface + ".java");
+ FileWriter fw = new FileWriter(dir + "/" + subdir + "/" + newIntface + ".java");
pw = new PrintWriter(new BufferedWriter(fw));
+ // Pass in set of methods and get import classes
+ Set<String> importClasses = getImportClasses(intMeth.getValue());
+ printImportStatements(importClasses);
// Write interface header
println("");
println("public interface " + newIntface + " {");
List<String> meths = intDecl.getMethods();
-
// Write methods
for (String method : intMeth.getValue()) {
print("public " + intDecl.getMethodType(method) + " " +
intDecl.getMethodId(method) + "(");
for (int i = 0; i < methParams.size(); i++) {
- print(methPrmTypes.get(i) + " " + methParams.get(i));
+ print(methPrmTypes.get(i) + " " + methParams.get(i));
// Check if this is the last element (don't print a comma)
if (i != methParams.size() - 1) {
print(", ");
public void generateCPlusInterfaces() throws IOException {
// Create a new directory
- createDirectory(dir);
+ createDirectories(dir, subdir);
for (Map.Entry<String,Set<String>> intMeth : mapCapabMethods.entrySet()) {
// Open a new file to write into
String newIntface = intMeth.getKey();
- FileWriter fw = new FileWriter(dir + "/" + newIntface + ".hpp");
+ FileWriter fw = new FileWriter(dir + "/" + subdir + "/" + newIntface + ".hpp");
pw = new PrintWriter(new BufferedWriter(fw));
// Write file headers
- println("#include<iostream>");
+ println("#include <iostream>");
+ // Pass in set of methods and get import classes
+ Set<String> includeClasses = getIncludeClasses(intMeth.getValue());
+ printIncludeStatements(includeClasses);
println("");
println("using namespace std;");
println("");
print("virtual " + convertType(intDecl.getMethodType(method)) + " " +
intDecl.getMethodId(method) + "(");
for (int i = 0; i < methParams.size(); i++) {
- print(convertType(methPrmTypes.get(i)) + " " + methParams.get(i));
+
+ String methPrmType = checkAndGetCplusType(methPrmTypes.get(i));
+ print(methPrmType + " " + methParams.get(i));
// Check if this is the last element (don't print a comma)
if (i != methParams.size() - 1) {
print(", ");
public void generateJavaStubClasses() throws IOException {
// Create a new directory
- createDirectory(dir);
+ createDirectories(dir, subdir);
for (Map.Entry<String,Set<String>> intMeth : mapCapabMethods.entrySet()) {
// Open a new file to write into
String newIntface = intMeth.getKey();
String newStubClass = newIntface + "_Stub";
- FileWriter fw = new FileWriter(dir + "/" + newStubClass + ".java");
+ FileWriter fw = new FileWriter(dir + "/" + subdir + "/" + newStubClass + ".java");
pw = new PrintWriter(new BufferedWriter(fw));
+ // Pass in set of methods and get import classes
+ Set<String> importClasses = getImportClasses(intMeth.getValue());
+ printImportStatements(importClasses);
// Write interface header
println("");
println("public class " + newStubClass + " implements " + newIntface + " {");
print("public " + intDecl.getMethodType(method) + " " +
intDecl.getMethodId(method) + "(");
for (int i = 0; i < methParams.size(); i++) {
- print(methPrmTypes.get(i) + " " + methParams.get(i));
+
+ print(methPrmTypes.get(i) + " " + methParams.get(i));
// Check if this is the last element (don't print a comma)
if (i != methParams.size() - 1) {
print(", ");
String retStmt = generateReturnStmt(intDecl.getMethodType(method));
println("return " + retStmt + ";");
}
- println("}");
- println("");
+ println("}"); println("");
}
println("}");
pw.close();
public void generateCPlusStubClasses() throws IOException {
// Create a new directory
- createDirectory(dir);
+ createDirectories(dir, subdir);
for (Map.Entry<String,Set<String>> intMeth : mapCapabMethods.entrySet()) {
// Open a new file to write into
String newIntface = intMeth.getKey();
String newStubClass = newIntface + "_Stub";
- FileWriter fw = new FileWriter(dir + "/" + newStubClass + ".hpp");
+ FileWriter fw = new FileWriter(dir + "/" + subdir + "/" + newStubClass + ".hpp");
pw = new PrintWriter(new BufferedWriter(fw));
// Write file headers
- println("#include<iostream>");
+ println("#include <iostream>");
println("#include \"" + newIntface + ".hpp\""); println("");
println("using namespace std;"); println("");
println("class " + newStubClass + " : public " + newIntface);
print(convertType(intDecl.getMethodType(method)) + " " +
intDecl.getMethodId(method) + "(");
for (int i = 0; i < methParams.size(); i++) {
- print(convertType(methPrmTypes.get(i)) + " " + methParams.get(i));
+
+ String methPrmType = checkAndGetCplusType(methPrmTypes.get(i));
+ print(methPrmType + " " + methParams.get(i));
// Check if this is the last element (don't print a comma)
if (i != methParams.size() - 1) {
print(", ");
/**
* setDirectory() set a new directory for stub files
*/
- public void setDirectory(String _dir) {
+ public void setDirectory(String _subdir) {
- dir = _dir;
+ subdir = _subdir;
}
*/
private String convertType(String jType) {
- // Generate dummy returns for now
- if (jType.equals("short")||
- jType.equals("int") ||
- jType.equals("long") ||
- jType.equals("char") ||
- jType.equals("float")||
- jType.equals("double")) {
-
- return jType;
- } else if ( jType.equals("String")) {
-
- return "string";
- } else if ( jType.equals("byte")) {
-
- return "char";
- } else if ( jType.equals("boolean")) {
-
- return "bool";
- } else {
- return jType;
- }
+ return mapPrimitives.get(jType);
}
}
+ // Create a directory and possibly a sub directory
+ private void createDirectories(String dir, String subdir) {
+
+ createDirectory(dir);
+ if (subdir != null) {
+ createDirectory(dir + "/" + subdir);
+ }
+ }
+
+
+ // Inserting array members into a Map object
+ // that maps arrKey to arrVal objects
+ private void arraysToMap(Map map, Object[] arrKey, Object[] arrVal) {
+
+ for(int i = 0; i < arrKey.length; i++) {
+
+ map.put(arrKey[i], arrVal[i]);
+ }
+ }
+
+
+ // Return parameter category, i.e. PRIMITIVES, NONPRIMITIVES, or USERDEFINED
+ private ParamCategory getParamCategory(String paramType) {
+
+ if (mapPrimitives.containsKey(paramType)) {
+ return ParamCategory.PRIMITIVES;
+ // We can either use mapNonPrimitivesJava or mapNonPrimitivesCplus here
+ } else if (mapNonPrimitivesJava.containsKey(getSimpleType(paramType))) {
+ return ParamCategory.NONPRIMITIVES;
+ } else
+ return ParamCategory.USERDEFINED;
+ }
+
+
+ // Return full class name for non-primitives to generate Java import statements
+ // e.g. java.util.Set for Set, java.util.Map for Map
+ private String getNonPrimitiveJavaClass(String paramNonPrimitives) {
+
+ return mapNonPrimitivesJava.get(paramNonPrimitives);
+ }
+
+
+ // Return full class name for non-primitives to generate Cplus include statements
+ // e.g. #include <set> for Set, #include <map> for Map
+ private String getNonPrimitiveCplusClass(String paramNonPrimitives) {
+
+ return mapNonPrimitivesCplus.get(paramNonPrimitives);
+ }
+
+
+ // Get simple types, e.g. HashSet for HashSet<...>
+ // Basically strip off the "<...>"
+ private String getSimpleType(String paramType) {
+
+ // Check if this is generics
+ if(paramType.contains("<")) {
+ String[] type = paramType.split("<");
+ return type[0];
+ } else
+ return paramType;
+ }
+
+
+ // Generate a set of classes for import statements
+ private Set<String> getImportClasses(Collection<String> methods) {
+
+ Set<String> importClasses = new HashSet<String>();
+ for (String method : methods) {
+ List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
+ for (String paramType : methPrmTypes) {
+
+ String simpleType = getSimpleType(paramType);
+ if (getParamCategory(simpleType) == ParamCategory.NONPRIMITIVES) {
+ importClasses.add(getNonPrimitiveJavaClass(simpleType));
+ }
+ }
+ }
+ return importClasses;
+ }
+
+
+ // Generate a set of classes for include statements
+ private Set<String> getIncludeClasses(Collection<String> methods) {
+
+ Set<String> includeClasses = new HashSet<String>();
+ for (String method : methods) {
+
+ List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
+ for (String paramType : methPrmTypes) {
+
+ String simpleType = getSimpleType(paramType);
+ if (getParamCategory(simpleType) == ParamCategory.NONPRIMITIVES) {
+ includeClasses.add(getNonPrimitiveCplusClass(simpleType));
+ }
+ }
+ }
+ return includeClasses;
+ }
+
+
+ private void printImportStatements(Set<String> importClasses) {
+
+ for(String cls : importClasses) {
+ println("import " + cls + ";");
+ }
+ }
+
+
+ private void printIncludeStatements(Set<String> includeClasses) {
+
+ for(String cls : includeClasses) {
+ println("#include <" + cls + ">");
+ }
+ }
+
+
+ // Get the C++ version of a non-primitive type
+ // e.g. set for Set and map for Map
+ // Input nonPrimitiveType has to be generics in format
+ private String[] getTypeOfGeneric(String nonPrimitiveType) {
+
+ // Handle <, >, and , for 2-type generic/template
+ String[] substr = nonPrimitiveType.split("<")[1].split(">")[0].split(",");
+ return substr;
+ }
+
+
+ private String checkAndGetCplusType(String paramType) {
+
+ if (getParamCategory(paramType) == ParamCategory.PRIMITIVES) {
+ return convertType(paramType);
+ } else if (getParamCategory(paramType) == ParamCategory.NONPRIMITIVES) {
+
+ // Check for generic/template format
+ if (paramType.contains("<") && paramType.contains(">")) {
+
+ String genericClass = getSimpleType(paramType);
+ String[] genericType = getTypeOfGeneric(paramType);
+ String cplusTemplate = null;
+ if (genericType.length == 1) // Generic/template with one type
+ cplusTemplate = getNonPrimitiveCplusClass(genericClass) +
+ "<" + convertType(genericType[0]) + ">";
+ else // Generic/template with two types
+ cplusTemplate = getNonPrimitiveCplusClass(genericClass) +
+ "<" + convertType(genericType[0]) + "," + convertType(genericType[1]) + ">";
+ return cplusTemplate;
+ } else
+ return getNonPrimitiveCplusClass(paramType);
+ } else
+ // Just return it as is if it's not non-primitives
+ return paramType;
+ }
+
+
+ // Get simple types, e.g. HashSet for HashSet<...>
+ // Basically strip off the "<...>"
+ private String checkAndGetParamClass(String paramType) {
+
+ // Check if this is generics
+ if(getParamCategory(paramType) == ParamCategory.USERDEFINED) {
+ // TODO: replace this with the proper stub interface name
+ return paramType + "Remote";
+ } else
+ return paramType;
+ }
+
+
public static void main(String[] args) throws Exception {
// Runtime options
comp.parsePolicyFile();
comp.getMethodsForIntface();
if (args.length == 2) {
+ comp.generateJavaLocalInterface();
comp.generateJavaInterfaces();
comp.generateJavaStubClasses();
+ comp.generateCplusLocalInterface();
comp.generateCPlusInterfaces();
comp.generateCPlusStubClasses();
} else {
} else
throw new Error("IoTCompiler: ERROR - please provide <directory> after option: " + args[i]);
if (args[i].equals("-java")) {
+ comp.generateJavaLocalInterface();
comp.generateJavaInterfaces();
comp.generateJavaStubClasses();
} else if (args[i].equals("-cplus")) {
+ comp.generateCplusLocalInterface();
comp.generateCPlusInterfaces();
comp.generateCPlusStubClasses();
} else