Adding handling for primitives, non-primitives, and user-defined types; doesn't handl...
[iot2.git] / iotjava / iotpolicy / IoTCompiler.java
index 757327707c9eebb08ef8a88c88c92918a69280fc..1107451282761cd3ab917065bb457b4163b4b79b 100644 (file)
@@ -3,6 +3,8 @@ package iotpolicy;
 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;
@@ -18,8 +20,8 @@ import iotpolicy.tree.CapabilityDecl;
 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.
  *
@@ -38,13 +40,117 @@ public class IoTCompiler {
        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
@@ -57,8 +163,15 @@ public class IoTCompiler {
                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;
        }
 
 
@@ -70,8 +183,15 @@ public class IoTCompiler {
                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;
        }
 
 
@@ -135,24 +255,129 @@ public class IoTCompiler {
        }
 
 
+       /**
+        * 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()) {
 
@@ -161,7 +386,7 @@ public class IoTCompiler {
                                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(", ");
@@ -184,15 +409,18 @@ public class IoTCompiler {
        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("");
@@ -207,7 +435,9 @@ public class IoTCompiler {
                                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(", ");
@@ -229,14 +459,17 @@ public class IoTCompiler {
        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 + " {");
@@ -249,7 +482,8 @@ public class IoTCompiler {
                                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(", ");
@@ -261,8 +495,7 @@ public class IoTCompiler {
                                        String retStmt = generateReturnStmt(intDecl.getMethodType(method));
                                        println("return " + retStmt + ";");
                                }
-                               println("}");
-                               println("");
+                               println("}"); println("");
                        }
                        println("}");
                        pw.close();
@@ -277,16 +510,16 @@ public class IoTCompiler {
        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);
@@ -303,7 +536,9 @@ public class IoTCompiler {
                                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(", ");
@@ -359,9 +594,9 @@ public class IoTCompiler {
        /**
         * setDirectory() set a new directory for stub files
         */
-       public void setDirectory(String _dir) {
+       public void setDirectory(String _subdir) {
 
-               dir = _dir;
+               subdir = _subdir;
        }
 
 
@@ -410,27 +645,7 @@ public class IoTCompiler {
         */
        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);
        }
 
 
@@ -479,6 +694,173 @@ public class IoTCompiler {
        }
 
 
+       // 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
@@ -508,8 +890,10 @@ public class IoTCompiler {
                                comp.parsePolicyFile();
                                comp.getMethodsForIntface();
                                if (args.length == 2) {
+                                       comp.generateJavaLocalInterface();
                                        comp.generateJavaInterfaces();
                                        comp.generateJavaStubClasses();
+                                       comp.generateCplusLocalInterface();
                                        comp.generateCPlusInterfaces();
                                        comp.generateCPlusStubClasses();
                                } else {
@@ -522,9 +906,11 @@ public class IoTCompiler {
                                                } 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