Adding permission checks for Java; fixing type translations for return and parameter...
[iot2.git] / iotjava / iotpolicy / IoTCompiler.java
1 package iotpolicy;
2
3 import java_cup.runtime.ComplexSymbolFactory;
4 import java_cup.runtime.ScannerBuffer;
5 import java.io.*;
6 import java.util.Arrays;
7 import java.util.ArrayList;
8 import java.util.Collection;
9 import java.util.Collections;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.Iterator;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16
17 import iotpolicy.parser.Lexer;
18 import iotpolicy.parser.Parser;
19 import iotpolicy.tree.ParseNode;
20 import iotpolicy.tree.ParseNodeVector;
21 import iotpolicy.tree.ParseTreeHandler;
22 import iotpolicy.tree.Declaration;
23 import iotpolicy.tree.DeclarationHandler;
24 import iotpolicy.tree.CapabilityDecl;
25 import iotpolicy.tree.InterfaceDecl;
26 import iotpolicy.tree.RequiresDecl;
27
28 import iotrmi.Java.IoTRMITypes;
29
30
31 /** Class IoTCompiler is the main interface/stub compiler for
32  *  files generation. This class calls helper classes
33  *  such as Parser, Lexer, InterfaceDecl, CapabilityDecl,
34  *  RequiresDecl, ParseTreeHandler, etc.
35  *
36  * @author      Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
37  * @version     1.0
38  * @since       2016-09-22
39  */
40 public class IoTCompiler {
41
42         /**
43          * Class properties
44          */
45         // Maps multiple interfaces to multiple objects of ParseTreeHandler
46         private Map<String,ParseTreeHandler> mapIntfacePTH;
47         private Map<String,DeclarationHandler> mapIntDeclHand;
48         private Map<String,Map<String,Set<String>>> mapInt2NewInts;
49         // Data structure to store our types (primitives and non-primitives) for compilation
50         private Map<String,String> mapPrimitives;
51         private Map<String,String> mapNonPrimitivesJava;
52         private Map<String,String> mapNonPrimitivesCplus;
53         // Other data structures
54         private Map<String,Integer> mapIntfaceObjId;    // Maps interface name to object Id
55         private PrintWriter pw;
56         private String dir;
57         private String subdir;
58
59         /**
60          * Class constants
61          */
62         private final static String OUTPUT_DIRECTORY = "output_files";
63
64         private enum ParamCategory {
65
66                 PRIMITIVES,             // All the primitive types, e.g. byte, short, int, long, etc.
67                 NONPRIMITIVES,  // Non-primitive types, e.g. Set, Map, List, etc.
68                 USERDEFINED             // Non-supported type by default; assumed as driver classes
69         }
70
71         /**
72          * Class constructors
73          */
74         public IoTCompiler() {
75
76                 mapIntfacePTH = new HashMap<String,ParseTreeHandler>();
77                 mapIntDeclHand = new HashMap<String,DeclarationHandler>();
78                 mapInt2NewInts = new HashMap<String,Map<String,Set<String>>>();
79                 mapIntfaceObjId = new HashMap<String,Integer>();
80                 mapPrimitives = new HashMap<String,String>();
81                         arraysToMap(mapPrimitives, IoTRMITypes.primitivesJava, IoTRMITypes.primitivesCplus);
82                 mapNonPrimitivesJava = new HashMap<String,String>();
83                         arraysToMap(mapNonPrimitivesJava, IoTRMITypes.nonPrimitivesJava, IoTRMITypes.nonPrimitiveJavaLibs);
84                 mapNonPrimitivesCplus = new HashMap<String,String>();
85                         arraysToMap(mapNonPrimitivesCplus, IoTRMITypes.nonPrimitivesJava, IoTRMITypes.nonPrimitivesCplus);
86                 pw = null;
87                 dir = OUTPUT_DIRECTORY;
88                 subdir = null;
89         }
90
91
92         /**
93          * setDataStructures() sets parse tree and other data structures based on policy files.
94          * <p>
95          * It also generates parse tree (ParseTreeHandler) and
96          * copies useful information from parse tree into
97          * InterfaceDecl, CapabilityDecl, and RequiresDecl 
98          * data structures.
99          * Additionally, the data structure handles are
100          * returned from tree-parsing for further process.
101          *
102          */
103         public void setDataStructures(String origInt, ParseNode pnPol, ParseNode pnReq) {
104
105                 ParseTreeHandler ptHandler = new ParseTreeHandler(origInt, pnPol, pnReq);
106                 DeclarationHandler decHandler = new DeclarationHandler();
107                 // Process ParseNode and generate Declaration objects
108                 ptHandler.processInterfaceDecl();
109                 InterfaceDecl intDecl = ptHandler.getInterfaceDecl();
110                 decHandler.addInterfaceDecl(origInt, intDecl);
111                 ptHandler.processCapabilityDecl();
112                 CapabilityDecl capDecl = ptHandler.getCapabilityDecl();
113                 decHandler.addCapabilityDecl(origInt, capDecl);
114                 ptHandler.processRequiresDecl();
115                 RequiresDecl reqDecl = ptHandler.getRequiresDecl();
116                 decHandler.addRequiresDecl(origInt, reqDecl);
117
118                 mapIntfacePTH.put(origInt, ptHandler);
119                 mapIntDeclHand.put(origInt, decHandler);
120                 // Set object Id counter to 0 for each interface
121                 mapIntfaceObjId.put(origInt, new Integer(0));
122                 //System.out.println("\nInterface: " + origInt + "\n\n");
123         }
124
125
126         /**
127          * getMethodsForIntface() reads for methods in the data structure
128          * <p>
129          * It is going to give list of methods for a certain interface
130          *              based on the declaration of capabilities.
131          */
132         public void getMethodsForIntface(String origInt) {
133
134                 ParseTreeHandler ptHandler = mapIntfacePTH.get(origInt);
135                 Map<String,Set<String>> mapNewIntMethods = new HashMap<String,Set<String>>();
136                 // Get set of new interfaces, e.g. CameraWithCaptureAndData
137                 // Generate this new interface with all the methods it needs
138                 //              from different capabilities it declares
139                 DeclarationHandler decHandler = mapIntDeclHand.get(origInt);
140                 RequiresDecl reqDecl = (RequiresDecl) decHandler.getRequiresDecl(origInt);
141                 Set<String> setIntfaces = reqDecl.getInterfaces();
142                 for (String strInt : setIntfaces) {
143
144                         // Initialize a set of methods
145                         Set<String> setMethods = new HashSet<String>();
146                         // Get list of capabilities, e.g. ImageCapture, VideoRecording, etc.
147                         List<String> listCapab = reqDecl.getCapabList(strInt);
148                         for (String strCap : listCapab) {
149
150                                 // Get list of methods for each capability
151                                 CapabilityDecl capDecl = (CapabilityDecl) decHandler.getCapabilityDecl(origInt);
152                                 List<String> listCapabMeth = capDecl.getMethods(strCap);
153                                 for (String strMeth : listCapabMeth) {
154
155                                         // Add methods into setMethods
156                                         // This is to also handle redundancies (say two capabilities
157                                         //              share the same methods)
158                                         setMethods.add(strMeth);
159                                 }
160                         }
161                         // Add interface and methods information into map
162                         mapNewIntMethods.put(strInt, setMethods);
163                 }
164                 // Map the map of interface-methods to the original interface
165                 mapInt2NewInts.put(origInt, mapNewIntMethods);
166         }
167
168
169         /**
170          * HELPER: writeMethodJavaInterface() writes the method of the interface
171          */
172         private void writeMethodJavaInterface(Collection<String> methods, InterfaceDecl intDecl) {
173
174                 for (String method : methods) {
175
176                         List<String> methParams = intDecl.getMethodParams(method);
177                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
178                         print("public " + intDecl.getMethodType(method) + " " +
179                                 intDecl.getMethodId(method) + "(");
180                         for (int i = 0; i < methParams.size(); i++) {
181                                 // Check for params with driver class types and exchange it 
182                                 //              with its remote interface
183                                 String paramType = checkAndGetParamClass(methPrmTypes.get(i), false);
184                                 print(paramType + " " + methParams.get(i));
185                                 // Check if this is the last element (don't print a comma)
186                                 if (i != methParams.size() - 1) {
187                                         print(", ");
188                                 }
189                         }
190                         println(");");
191                 }
192         }
193
194
195         /**
196          * generateJavaLocalInterface() writes the local interface and provides type-checking.
197          * <p>
198          * It needs to rewrite and exchange USERDEFINED types in input parameters of stub
199          * and original interfaces, e.g. exchange Camera and CameraWithVideoAndRecording.
200          * The local interface has to be the input parameter for the stub and the stub 
201          * interface has to be the input parameter for the local class.
202          */
203         public void generateJavaLocalInterfaces() throws IOException {
204
205                 // Create a new directory
206                 createDirectory(dir);
207                 for (String intface : mapIntfacePTH.keySet()) {
208                         // Open a new file to write into
209                         FileWriter fw = new FileWriter(dir + "/" + intface + ".java");
210                         pw = new PrintWriter(new BufferedWriter(fw));
211                         // Pass in set of methods and get import classes
212                         DeclarationHandler decHandler = mapIntDeclHand.get(intface);
213                         InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
214                         List<String> methods = intDecl.getMethods();
215                         Set<String> importClasses = getImportClasses(methods, intDecl);
216                         printImportStatements(importClasses);
217                         // Write interface header
218                         println("");
219                         println("public interface " + intface + " {");
220                         // Write methods
221                         writeMethodJavaInterface(methods, intDecl);
222                         println("}");
223                         pw.close();
224                         System.out.println("IoTCompiler: Generated local interface " + intface + ".java...");
225                 }
226         }
227
228
229         /**
230          * generateJavaInterfaces() generate stub interfaces based on the methods list in Java
231          */
232         public void generateJavaInterfaces() throws IOException {
233
234                 // Create a new directory
235                 String path = createDirectories(dir, subdir);
236                 for (String intface : mapIntfacePTH.keySet()) {
237
238                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
239                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
240
241                                 // Open a new file to write into
242                                 String newIntface = intMeth.getKey();
243                                 FileWriter fw = new FileWriter(path + "/" + newIntface + ".java");
244                                 pw = new PrintWriter(new BufferedWriter(fw));
245                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
246                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
247                                 // Pass in set of methods and get import classes
248                                 Set<String> importClasses = getImportClasses(intMeth.getValue(), intDecl);
249                                 printImportStatements(importClasses);
250                                 // Write interface header
251                                 println("");
252                                 println("public interface " + newIntface + " {");
253                                 // Write methods
254                                 writeMethodJavaInterface(intMeth.getValue(), intDecl);
255                                 println("}");
256                                 pw.close();
257                                 System.out.println("IoTCompiler: Generated interface " + newIntface + ".java...");
258                         }
259                 }
260         }
261
262
263         /**
264          * HELPER: writePropertiesJavaStub() writes the properties of the stub class
265          */
266         private void writePropertiesJavaStub(String intface) {
267
268                 println("private IoTRMICall rmiCall;");
269                 //println("private IoTRMIObject rmiObj;");
270                 println("private String address;");
271                 println("private int[] ports;\n");
272                 // Get the object Id
273                 Integer objId = mapIntfaceObjId.get(intface);
274                 println("private final static int objectId = " + objId + ";");
275                 mapIntfaceObjId.put(intface, objId++);
276                 println("\n");
277         }
278
279
280         /**
281          * HELPER: writeConstructorJavaStub() writes the constructor of the stub class
282          */
283         private void writeConstructorJavaStub(String intface) {
284
285                 println("public " + intface + "(int _port, String _address, int _rev, int[] _ports) throws Exception {");
286                 println("address = _address;");
287                 println("ports = _ports;");
288                 println("rmiCall = new IoTRMICall(_port, _address, _rev);");
289                 println("}\n");
290         }
291
292
293         /**
294          * HELPER: writeStdMethodBodyJavaStub() writes the standard method body in the stub class
295          */
296         private void writeStdMethodBodyJavaStub(InterfaceDecl intDecl, List<String> methParams,
297                         List<String> methPrmTypes, String method) {
298
299                 println("int methodId = " + intDecl.getMethodNumId(method) + ";");
300                 String retType = intDecl.getMethodType(method);
301                 println("Class<?> retType = " + getSimpleType(retType) + ".class;");
302                 // Generate array of parameter types
303                 print("Class<?>[] paramCls = new Class<?>[] { ");
304                 for (int i = 0; i < methParams.size(); i++) {
305                         String paramType = checkAndGetArray(methPrmTypes.get(i), methParams.get(i));
306                         print(getSimpleType(paramType) + ".class");
307                         // Check if this is the last element (don't print a comma)
308                         if (i != methParams.size() - 1) {
309                                 print(", ");
310                         }
311                 }
312                 println(" };");
313                 // Generate array of parameter objects
314                 print("Object[] paramObj = new Object[] { ");
315                 for (int i = 0; i < methParams.size(); i++) {
316                         print(getSimpleIdentifier(methParams.get(i)));
317                         // Check if this is the last element (don't print a comma)
318                         if (i != methParams.size() - 1) {
319                                 print(", ");
320                         }
321                 }
322                 println(" };");
323                 // Check if this is "void"
324                 if (retType.equals("void")) {
325                         println("rmiCall.remoteCall(objectId, methodId, retType, null, paramCls, paramObj);");
326                 } else { // We do have a return value
327                 // Check if the return value NONPRIMITIVES
328                         if (getParamCategory(retType) == ParamCategory.NONPRIMITIVES) {
329                                 String[] retGenValType = getTypeOfGeneric(retType);
330                                 println("Class<?> retGenValType = " + retGenValType[0] + ".class;");
331                                 println("Object retObj = rmiCall.remoteCall(objectId, methodId, retType, retGenValType, paramCls, paramObj);");
332                                 println("return (" + retType + ")retObj;");
333                         } else {
334                                 println("Object retObj = rmiCall.remoteCall(objectId, methodId, retType, null, paramCls, paramObj);");
335                                 println("return (" + retType + ")retObj;");
336                         }
337                 }
338         }
339
340
341         /**
342          * HELPER: writeMethodJavaStub() writes the method of the stub class
343          */
344         private void writeMethodJavaStub(Collection<String> methods, InterfaceDecl intDecl) {
345
346                 for (String method : methods) {
347
348                         List<String> methParams = intDecl.getMethodParams(method);
349                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
350                         print("public " + intDecl.getMethodType(method) + " " +
351                                 intDecl.getMethodId(method) + "(");
352                         for (int i = 0; i < methParams.size(); i++) {
353
354                                 print(methPrmTypes.get(i) + " " + methParams.get(i));
355                                 // Check if this is the last element (don't print a comma)
356                                 if (i != methParams.size() - 1) {
357                                         print(", ");
358                                 }
359                         }
360                         println(") {");
361                         // Now, write the body of stub!
362                         writeStdMethodBodyJavaStub(intDecl, methParams, methPrmTypes, method);
363                         println("}\n");
364                 }
365         }
366
367
368         /**
369          * generateJavaStubClasses() generate stubs based on the methods list in Java
370          */
371         public void generateJavaStubClasses() throws IOException {
372
373                 // Create a new directory
374                 String path = createDirectories(dir, subdir);
375                 for (String intface : mapIntfacePTH.keySet()) {
376
377                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
378                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
379
380                                 // Open a new file to write into
381                                 String newIntface = intMeth.getKey();
382                                 String newStubClass = newIntface + "_Stub";
383                                 FileWriter fw = new FileWriter(path + "/" + newStubClass + ".java");
384                                 pw = new PrintWriter(new BufferedWriter(fw));
385                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
386                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
387                                 // Pass in set of methods and get import classes
388                                 Set<String> importClasses = getImportClasses(intMeth.getValue(), intDecl);
389                                 List<String> stdImportClasses = getStandardJavaImportClasses();
390                                 List<String> allImportClasses = getAllImportClasses(stdImportClasses, importClasses);
391                                 printImportStatements(allImportClasses); println("");
392                                 // Write interface header
393                                 println("public class " + newStubClass + " implements " + newIntface + " {\n");
394                                 // Write properties
395                                 writePropertiesJavaStub(intface);
396                                 // Write constructor
397                                 writeConstructorJavaStub(newStubClass);
398                                 // Write methods
399                                 writeMethodJavaStub(intMeth.getValue(), intDecl);
400                                 println("}");
401                                 pw.close();
402                                 System.out.println("IoTCompiler: Generated stub class " + newStubClass + ".java...");
403                         }
404                 }
405         }
406
407
408         /**
409          * HELPER: writeMethodCplusInterface() writes the method of the interface
410          */
411         private void writeMethodCplusInterface(Collection<String> methods, InterfaceDecl intDecl) {
412
413                 for (String method : methods) {
414
415                         List<String> methParams = intDecl.getMethodParams(method);
416                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
417                         print("virtual " + checkAndGetCplusType(intDecl.getMethodType(method)) + " " +
418                                 intDecl.getMethodId(method) + "(");
419                         for (int i = 0; i < methParams.size(); i++) {
420                                 // Check for params with driver class types and exchange it 
421                                 //              with its remote interface
422                                 String paramType = checkAndGetParamClass(methPrmTypes.get(i), true);
423                                 paramType = checkAndGetCplusType(paramType);
424                                 // Check for arrays - translate into vector in C++
425                                 String paramComplete = checkAndGetCplusArray(paramType, methParams.get(i));
426                                 print(paramComplete);
427                                 // Check if this is the last element (don't print a comma)
428                                 if (i != methParams.size() - 1) {
429                                         print(", ");
430                                 }
431                         }
432                         println(") = 0;");
433                 }
434         }
435
436
437         /**
438          * generateCplusLocalInterfaces() writes the local interfaces and provides type-checking.
439          * <p>
440          * It needs to rewrite and exchange USERDEFINED types in input parameters of stub
441          * and original interfaces, e.g. exchange Camera and CameraWithVideoAndRecording.
442          * The local interface has to be the input parameter for the stub and the stub 
443          * interface has to be the input parameter for the local class.
444          */
445         public void generateCplusLocalInterfaces() throws IOException {
446
447                 // Create a new directory
448                 createDirectory(dir);
449                 for (String intface : mapIntfacePTH.keySet()) {
450                         // Open a new file to write into
451                         FileWriter fw = new FileWriter(dir + "/" + intface + ".hpp");
452                         pw = new PrintWriter(new BufferedWriter(fw));
453                         // Write file headers
454                         println("#ifndef _" + intface.toUpperCase() + "_HPP__");
455                         println("#define _" + intface.toUpperCase() + "_HPP__");
456                         println("#include <iostream>");
457                         // Pass in set of methods and get include classes
458                         DeclarationHandler decHandler = mapIntDeclHand.get(intface);
459                         InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
460                         List<String> methods = intDecl.getMethods();
461                         Set<String> includeClasses = getIncludeClasses(methods, intDecl);
462                         printIncludeStatements(includeClasses); println("");
463                         println("using namespace std;\n");
464                         println("class " + intface); println("{");
465                         println("public:");
466                         // Write methods
467                         writeMethodCplusInterface(methods, intDecl);
468                         println("};");
469                         println("#endif");
470                         pw.close();
471                         System.out.println("IoTCompiler: Generated local interface " + intface + ".hpp...");
472                 }
473         }
474
475
476         /**
477          * generateCPlusInterfaces() generate stub interfaces based on the methods list in C++
478          * <p>
479          * For C++ we use virtual classe as interface
480          */
481         public void generateCPlusInterfaces() throws IOException {
482
483                 // Create a new directory
484                 String path = createDirectories(dir, subdir);
485                 for (String intface : mapIntfacePTH.keySet()) {
486
487                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
488                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
489
490                                 // Open a new file to write into
491                                 String newIntface = intMeth.getKey();
492                                 FileWriter fw = new FileWriter(path + "/" + newIntface + ".hpp");
493                                 pw = new PrintWriter(new BufferedWriter(fw));
494                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
495                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
496                                 // Write file headers
497                                 println("#ifndef _" + newIntface.toUpperCase() + "_HPP__");
498                                 println("#define _" + newIntface.toUpperCase() + "_HPP__");
499                                 println("#include <iostream>");
500                                 // Pass in set of methods and get import classes
501                                 Set<String> includeClasses = getIncludeClasses(intMeth.getValue(), intDecl);
502                                 List<String> stdIncludeClasses = getStandardCplusIncludeClasses();
503                                 List<String> allIncludeClasses = getAllImportClasses(stdIncludeClasses, includeClasses);
504                                 printIncludeStatements(allIncludeClasses); println("");                 
505                                 println("using namespace std;\n");
506                                 println("class " + newIntface);
507                                 println("{");
508                                 println("public:");
509                                 // Write methods
510                                 writeMethodCplusInterface(intMeth.getValue(), intDecl);
511                                 println("};");
512                                 println("#endif");
513                                 pw.close();
514                                 System.out.println("IoTCompiler: Generated interface " + newIntface + ".hpp...");
515                         }
516                 }
517         }
518
519
520         /**
521          * HELPER: writeMethodCplusStub() writes the method of the stub
522          */
523         private void writeMethodCplusStub(Collection<String> methods, InterfaceDecl intDecl) {
524
525                 for (String method : methods) {
526
527                         List<String> methParams = intDecl.getMethodParams(method);
528                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
529                         print(checkAndGetCplusType(intDecl.getMethodType(method)) + " " +
530                                 intDecl.getMethodId(method) + "(");
531                         for (int i = 0; i < methParams.size(); i++) {
532                                 String methPrmType = checkAndGetCplusType(methPrmTypes.get(i));
533                                 String methParamComplete = checkAndGetCplusArray(methPrmType, methParams.get(i));
534                                 print(methParamComplete);
535                                 // Check if this is the last element (don't print a comma)
536                                 if (i != methParams.size() - 1) {
537                                         print(", ");
538                                 }
539                         }
540                         println(") { ");
541                         writeStdMethodBodyCplusStub(intDecl, methParams, methPrmTypes, method);
542                         println("}\n");
543                 }
544         }
545
546
547         /**
548          * HELPER: writeStdMethodBodyCplusStub() writes the standard method body in the stub class
549          */
550         private void writeStdMethodBodyCplusStub(InterfaceDecl intDecl, List<String> methParams,
551                         List<String> methPrmTypes, String method) {
552
553                 println("int numParam = " + methParams.size() + ";");
554                 println("int methodId = " + intDecl.getMethodNumId(method) + ";");
555                 String retType = intDecl.getMethodType(method);
556                 println("string retType = \"" + checkAndGetCplusType(retType) + "\";");
557                 // Generate array of parameter types
558                 print("string paramCls[] = { ");
559                 for (int i = 0; i < methParams.size(); i++) {
560                         String paramType = checkAndGetArray(methPrmTypes.get(i), methParams.get(i));
561                         print("\"" + getSimpleType(paramType) + "\"");
562                         // Check if this is the last element (don't print a comma)
563                         if (i != methParams.size() - 1) {
564                                 print(", ");
565                         }
566                 }
567                 println(" };");
568                 // Generate array of parameter objects
569                 print("void* paramObj[] = { ");
570                 for (int i = 0; i < methParams.size(); i++) {
571                         print("&" + checkAndGetCplusType(getSimpleIdentifier(methParams.get(i))));
572                         // Check if this is the last element (don't print a comma)
573                         if (i != methParams.size() - 1) {
574                                 print(", ");
575                         }
576                 }
577                 println(" };");
578                 // Check if this is "void"
579                 if (retType.equals("void")) {
580                         println("void* retObj = NULL;");
581                         println("rmiCall->remoteCall(objectId, methodId, retType, paramCls, paramObj, numParam, retObj);");
582                 } else { // We do have a return value
583                         if (getParamCategory(retType) == ParamCategory.NONPRIMITIVES)
584                                 println(checkAndGetCplusType(retType) + " retVal;");
585                         else
586                                 println(checkAndGetCplusType(retType) + " retVal = " + generateCplusInitializer(retType) + ";");
587                         println("void* retObj = &retVal;");
588                         println("rmiCall->remoteCall(objectId, methodId, retType, paramCls, paramObj, numParam, retObj);");
589                         println("return retVal;");
590                 }
591         }
592
593
594         /**
595          * HELPER: writePropertiesCplusStub() writes the properties of the stub class
596          */
597         private void writePropertiesCplusStub(String intface) {
598
599                 println("IoTRMICall\t\t\t*rmiCall;");
600                 //println("IoTRMIObject\t\t\t*rmiObj;");
601                 println("string\t\t\t\taddress;");
602                 println("vector<int>\t\t\tports;\n");
603                 // Get the object Id
604                 Integer objId = mapIntfaceObjId.get(intface);
605                 println("const static int\tobjectId = " + objId + ";");
606                 mapIntfaceObjId.put(intface, objId++);
607                 println("\n");
608         }
609
610
611         /**
612          * HELPER: writeConstructorCplusStub() writes the constructor of the stub class
613          */
614         private void writeConstructorCplusStub(String newStubClass) {
615
616                 println(newStubClass + 
617                         "(int _port, const char* _address, int _rev, bool* _bResult, vector<int> _ports) {");
618                 println("address = _address;");
619                 println("ports = _ports;");
620                 println("rmiCall = new IoTRMICall(_port, _address, _rev, _bResult);");
621                 println("}\n");
622         }
623
624
625         /**
626          * HELPER: writeDeconstructorCplusStub() writes the deconstructor of the stub class
627          */
628         private void writeDeconstructorCplusStub(String newStubClass) {
629
630                 println("~" + newStubClass + "() {");
631                 println("if (rmiCall != NULL) {");
632                 println("delete rmiCall;");
633                 println("rmiCall = NULL;");
634                 println("}");
635                 println("}");
636                 println("");
637                 // Check if this is callback!!! and print "delete rmiObj and vecCBObj"
638         }
639
640
641         /**
642          * generateCPlusStubClasses() generate stubs based on the methods list in C++
643          */
644         public void generateCPlusStubClasses() throws IOException {
645
646                 // Create a new directory
647                 String path = createDirectories(dir, subdir);
648                 for (String intface : mapIntfacePTH.keySet()) {
649
650                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
651                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
652                                 // Open a new file to write into
653                                 String newIntface = intMeth.getKey();
654                                 String newStubClass = newIntface + "_Stub";
655                                 FileWriter fw = new FileWriter(path + "/" + newStubClass + ".hpp");
656                                 pw = new PrintWriter(new BufferedWriter(fw));
657                                 // Write file headers
658                                 println("#ifndef _" + newStubClass.toUpperCase() + "_HPP__");
659                                 println("#define _" + newStubClass.toUpperCase() + "_HPP__");
660                                 println("#include <iostream>");
661                                 println("#include \"" + newIntface + ".hpp\""); println("");            
662                                 println("using namespace std;"); println("");
663                                 println("class " + newStubClass + " : public " + newIntface); println("{");
664                                 println("private:\n");
665                                 writePropertiesCplusStub(intface);
666                                 println("public:\n");
667                                 // Add default constructor and destructor
668                                 println(newStubClass + "() { }"); println("");
669                                 writeConstructorCplusStub(newStubClass);
670                                 writeDeconstructorCplusStub(newStubClass);
671                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
672                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
673                                 // Write methods
674                                 writeMethodCplusStub(intMeth.getValue(), intDecl);
675                                 print("}"); println(";");
676                                 println("#endif");
677                                 pw.close();
678                                 System.out.println("IoTCompiler: Generated stub class " + newIntface + ".hpp...");
679                         }
680                 }
681         }
682
683
684         /**
685          * generateInitializer() generate initializer based on type
686          */
687         public String generateCplusInitializer(String type) {
688
689                 // Generate dummy returns for now
690                 if (type.equals("short")||
691                         type.equals("int")      ||
692                         type.equals("long") ||
693                         type.equals("float")||
694                         type.equals("double")) {
695
696                         return "0";
697                 } else if ( type.equals("String") ||
698                                         type.equals("string")) {
699   
700                         return "\"\"";
701                 } else if ( type.equals("char") ||
702                                         type.equals("byte")) {
703
704                         return "\' \'";
705                 } else if ( type.equals("boolean")) {
706
707                         return "false";
708                 } else {
709                         return "NULL";
710                 }
711         }
712
713
714         /**
715          * generateReturnStmt() generate return statement based on methType
716          */
717         public String generateReturnStmt(String methType) {
718
719                 // Generate dummy returns for now
720                 if (methType.equals("short")||
721                         methType.equals("int")  ||
722                         methType.equals("long") ||
723                         methType.equals("float")||
724                         methType.equals("double")) {
725
726                         return "1";
727                 } else if ( methType.equals("String")) {
728   
729                         return "\"a\"";
730                 } else if ( methType.equals("char") ||
731                                         methType.equals("byte")) {
732
733                         return "\'a\'";
734                 } else if ( methType.equals("boolean")) {
735
736                         return "true";
737                 } else {
738                         return "null";
739                 }
740         }
741
742
743         /**
744          * setDirectory() sets a new directory for stub files
745          */
746         public void setDirectory(String _subdir) {
747
748                 subdir = _subdir;
749         }
750
751
752         /**
753          * printUsage() prints the usage of this compiler
754          */
755         public static void printUsage() {
756
757                 System.out.println();
758                 System.out.println("Sentinel interface and stub compiler version 1.0");
759                 System.out.println("Copyright (c) 2015-2016 University of California, Irvine - Programming Language Group.");
760                 System.out.println("All rights reserved.");
761                 System.out.println("Usage:");
762                 System.out.println("\tjava IoTCompiler -help / --help / -h\n");
763                 System.out.println("\t\tDisplay this help texts\n\n");
764                 System.out.println("\tjava IoTCompiler [<main-policy-file> <req-policy-file>]");
765                 System.out.println("\tjava IoTCompiler [<main-policy-file> <req-policy-file>] [options]\n");
766                 System.out.println("\t\tTake one or more pairs of main-req policy files, and generate Java and/or C++ files\n");
767                 System.out.println("Options:");
768                 System.out.println("\t-java\t<directory>\tGenerate Java stub files");
769                 System.out.println("\t-cplus\t<directory>\tGenerate C++ stub files");
770                 System.out.println();
771         }
772
773
774         /**
775          * parseFile() prepares Lexer and Parser objects, then parses the file
776          */
777         public static ParseNode parseFile(String file) {
778
779                 ParseNode pn = null;
780                 try {
781                         ComplexSymbolFactory csf = new ComplexSymbolFactory();
782                         ScannerBuffer lexer = 
783                                 new ScannerBuffer(new Lexer(new BufferedReader(new FileReader(file)),csf));
784                         Parser parse = new Parser(lexer,csf);
785                         pn = (ParseNode) parse.parse().value;
786                 } catch (Exception e) {
787                         e.printStackTrace();
788                         throw new Error("IoTCompiler: ERROR parsing policy file or wrong command line option: " + file);
789                 }
790
791                 return pn;
792         }
793
794
795         /**================
796          * Helper functions
797          **================
798          */
799         boolean newline=true;
800         int tablevel=0;
801
802         private void print(String str) {
803                 if (newline) {
804                         int tab=tablevel;
805                         if (str.equals("}"))
806                                 tab--;
807                         for(int i=0; i<tab; i++)
808                                 pw.print("\t");
809                 }
810                 pw.print(str);
811                 updatetabbing(str);
812                 newline=false;
813         }
814
815
816         /**
817          * This function converts Java to C++ type for compilation
818          */
819         private String convertType(String jType) {
820
821                 return mapPrimitives.get(jType);
822         }
823
824
825         private void println(String str) {
826                 if (newline) {
827                         int tab = tablevel;
828                         if (str.equals("}"))
829                                 tab--;
830                         for(int i=0; i<tab; i++)
831                                 pw.print("\t");
832                 }
833                 pw.println(str);
834                 updatetabbing(str);
835                 newline = true;
836         }
837
838
839         private void updatetabbing(String str) {
840                 tablevel+=count(str,'{')-count(str,'}');
841         }
842
843
844         private int count(String str, char key) {
845                 char[] array = str.toCharArray();
846                 int count = 0;
847                 for(int i=0; i<array.length; i++) {
848                         if (array[i] == key)
849                                 count++;
850                 }
851                 return count;
852         }
853
854
855         private void createDirectory(String dirName) {
856
857                 File file = new File(dirName);
858                 if (!file.exists()) {
859                         if (file.mkdir()) {
860                                 System.out.println("IoTCompiler: Directory " + dirName + " has been created!");
861                         } else {
862                                 System.out.println("IoTCompiler: Failed to create directory " + dirName + "!");
863                         }
864                 } else {
865                         System.out.println("IoTCompiler: Directory " + dirName + " exists...");
866                 }
867         }
868
869
870         // Create a directory and possibly a sub directory
871         private String createDirectories(String dir, String subdir) {
872
873                 String path = dir;
874                 createDirectory(path);
875                 if (subdir != null) {
876                         path = path + "/" + subdir;
877                         createDirectory(path);
878                 }
879                 return path;
880         }
881
882
883         // Inserting array members into a Map object
884         // that maps arrKey to arrVal objects
885         private void arraysToMap(Map map, Object[] arrKey, Object[] arrVal) {
886
887                 for(int i = 0; i < arrKey.length; i++) {
888
889                         map.put(arrKey[i], arrVal[i]);
890                 }
891         }
892
893
894         // Return parameter category, i.e. PRIMITIVES, NONPRIMITIVES, or USERDEFINED
895         private ParamCategory getParamCategory(String paramType) {
896
897                 if (mapPrimitives.containsKey(paramType)) {
898                         return ParamCategory.PRIMITIVES;
899                 // We can either use mapNonPrimitivesJava or mapNonPrimitivesCplus here
900                 } else if (mapNonPrimitivesJava.containsKey(getSimpleType(paramType))) {
901                         return ParamCategory.NONPRIMITIVES;
902                 } else
903                         return ParamCategory.USERDEFINED;
904         }
905
906
907         // Return full class name for non-primitives to generate Java import statements
908         // e.g. java.util.Set for Set, java.util.Map for Map
909         private String getNonPrimitiveJavaClass(String paramNonPrimitives) {
910
911                 return mapNonPrimitivesJava.get(paramNonPrimitives);
912         }
913
914
915         // Return full class name for non-primitives to generate Cplus include statements
916         // e.g. #include <set> for Set, #include <map> for Map
917         private String getNonPrimitiveCplusClass(String paramNonPrimitives) {
918
919                 return mapNonPrimitivesCplus.get(paramNonPrimitives);
920         }
921
922
923         // Get simple types, e.g. HashSet for HashSet<...>
924         // Basically strip off the "<...>"
925         private String getSimpleType(String paramType) {
926
927                 // Check if this is generics
928                 if(paramType.contains("<")) {
929                         String[] type = paramType.split("<");
930                         return type[0];
931                 } else
932                         return paramType;
933         }
934
935
936         // Generate a set of standard classes for import statements
937         private List<String> getStandardJavaImportClasses() {
938
939                 List<String> importClasses = new ArrayList<String>();
940                 // Add the standard list first
941                 importClasses.add("java.io.IOException");
942                 importClasses.add("java.util.List");
943                 importClasses.add("java.util.ArrayList");
944                 importClasses.add("iotrmi.Java.IoTRMICall");
945                 importClasses.add("iotrmi.Java.IoTRMIObject");
946
947                 return importClasses;
948         }
949
950
951         // Generate a set of standard classes for import statements
952         private List<String> getStandardCplusIncludeClasses() {
953
954                 List<String> importClasses = new ArrayList<String>();
955                 // Add the standard list first
956                 importClasses.add("<vector>");
957                 importClasses.add("\"IoTRMICall.hpp\"");
958                 importClasses.add("\"IoTRMIObject.hpp\"");
959
960                 return importClasses;
961         }
962
963
964         // Generate a set of standard classes for import statements
965         private List<String> getAllImportClasses(Collection<String> stdImportClasses, Collection<String> importClasses) {
966
967                 List<String> allImportClasses = new ArrayList<String>(stdImportClasses);
968                 // Iterate over the list of import classes
969                 for (String str : importClasses) {
970                         if (!stdImportClasses.contains(str)) {
971                                 stdImportClasses.add(str);
972                         }
973                 }
974
975                 return allImportClasses;
976         }
977
978
979
980         // Generate a set of classes for import statements
981         private Set<String> getImportClasses(Collection<String> methods, InterfaceDecl intDecl) {
982
983                 Set<String> importClasses = new HashSet<String>();
984                 for (String method : methods) {
985                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
986                         for (String paramType : methPrmTypes) {
987
988                                 String simpleType = getSimpleType(paramType);
989                                 if (getParamCategory(simpleType) == ParamCategory.NONPRIMITIVES) {
990                                         importClasses.add(getNonPrimitiveJavaClass(simpleType));
991                                 }
992                         }
993                 }
994                 return importClasses;
995         }
996
997
998         // Generate a set of classes for include statements
999         private Set<String> getIncludeClasses(Collection<String> methods, InterfaceDecl intDecl) {
1000
1001                 Set<String> includeClasses = new HashSet<String>();
1002                 for (String method : methods) {
1003
1004                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
1005                         List<String> methParams = intDecl.getMethodParams(method);
1006                         for (int i = 0; i < methPrmTypes.size(); i++) {
1007
1008                                 String simpleType = getSimpleType(methPrmTypes.get(i));
1009                                 String param = methParams.get(i);
1010                                 if (getParamCategory(simpleType) == ParamCategory.NONPRIMITIVES) {
1011                                         includeClasses.add("<" + getNonPrimitiveCplusClass(simpleType) + ">");
1012                                 } else if (getParamCategory(simpleType) == ParamCategory.USERDEFINED) {
1013                                         includeClasses.add("\"" + exchangeParamType(simpleType) + ".hpp\"");
1014                                 } else if (param.contains("[]")) {
1015                                 // Check if this is array for C++; translate into vector
1016                                         includeClasses.add("<vector>");
1017                                 }
1018                         }
1019                 }
1020                 return includeClasses;
1021         }
1022
1023
1024         private void printImportStatements(Collection<String> importClasses) {
1025
1026                 for(String cls : importClasses) {
1027                         println("import " + cls + ";");
1028                 }
1029         }
1030
1031
1032         private void printIncludeStatements(Collection<String> includeClasses) {
1033
1034                 for(String cls : includeClasses) {
1035                         println("#include " + cls);
1036                 }
1037         }
1038
1039
1040         // Get the C++ version of a non-primitive type
1041         // e.g. set for Set and map for Map
1042         // Input nonPrimitiveType has to be generics in format
1043         private String[] getTypeOfGeneric(String nonPrimitiveType) {
1044
1045                 // Handle <, >, and , for 2-type generic/template
1046                 String[] substr = nonPrimitiveType.split("<")[1].split(">")[0].split(",");
1047                 return substr;
1048         }
1049
1050
1051         // This helper function strips off array declaration, e.g. D[] becomes D
1052         private String getSimpleIdentifier(String ident) {
1053
1054                 // Handle [ for array declaration
1055                 String substr = ident;
1056                 if (ident.contains("[]")) {
1057                         substr = ident.split("\\[\\]")[0];
1058                 }
1059                 return substr;
1060         }
1061
1062
1063         private String checkAndGetCplusType(String paramType) {
1064
1065                 if (getParamCategory(paramType) == ParamCategory.PRIMITIVES) {
1066                         return convertType(paramType);
1067                 } else if (getParamCategory(paramType) == ParamCategory.NONPRIMITIVES) {
1068
1069                         // Check for generic/template format
1070                         if (paramType.contains("<") && paramType.contains(">")) {
1071
1072                                 String genericClass = getSimpleType(paramType);
1073                                 String[] genericType = getTypeOfGeneric(paramType);
1074                                 String cplusTemplate = null;
1075                                 if (genericType.length == 1) // Generic/template with one type
1076                                         cplusTemplate = getNonPrimitiveCplusClass(genericClass) + 
1077                                                 "<" + convertType(genericType[0]) + ">";
1078                                 else // Generic/template with two types
1079                                         cplusTemplate = getNonPrimitiveCplusClass(genericClass) + 
1080                                                 "<" + convertType(genericType[0]) + "," + convertType(genericType[1]) + ">";
1081                                 return cplusTemplate;
1082                         } else
1083                                 return getNonPrimitiveCplusClass(paramType);
1084                 } else
1085                         // Just return it as is if it's not non-primitives
1086                         return paramType;
1087         }
1088
1089
1090         // Detect array declaration, e.g. int A[],
1091         //              then generate "int A[]" in C++ as "vector<int> A"
1092         private String checkAndGetCplusArray(String paramType, String param) {
1093
1094                 String paramComplete = null;
1095                 // Check for array declaration
1096                 if (param.contains("[]")) {
1097                         paramComplete = "vector<" + paramType + "> " + param.replace("[]","");
1098                 } else
1099                         // Just return it as is if it's not an array
1100                         paramComplete = paramType + " " + param;
1101
1102                 return paramComplete;
1103         }
1104
1105
1106         // Detect array declaration, e.g. int A[],
1107         //              then generate type "int[]"
1108         private String checkAndGetArray(String paramType, String param) {
1109
1110                 String paramTypeRet = null;
1111                 // Check for array declaration
1112                 if (param.contains("[]")) {
1113                         paramTypeRet = paramType + "[]";
1114                 } else
1115                         // Just return it as is if it's not an array
1116                         paramTypeRet = paramType;
1117
1118                 return paramTypeRet;
1119         }
1120
1121
1122         // Get simple types, e.g. HashSet for HashSet<...>
1123         // Basically strip off the "<...>"
1124         private String checkAndGetParamClass(String paramType, boolean needPtr) {
1125
1126                 // Check if this is generics
1127                 if(getParamCategory(paramType) == ParamCategory.USERDEFINED) {
1128                         // If true then return with pointer (C++)
1129                         if (needPtr)
1130                                 return exchangeParamType(paramType) + "*";
1131                         else    // Java, so no pointer needed
1132                                 return exchangeParamType(paramType);
1133                 } else
1134                         return paramType;
1135         }
1136
1137
1138         // Returns the other interface for type-checking purposes for USERDEFINED
1139         //              classes based on the information provided in multiple policy files
1140         // e.g. return CameraWithXXX instead of Camera
1141         private String exchangeParamType(String intface) {
1142
1143                 // Param type that's passed is the interface name we need to look for
1144                 //              in the map of interfaces, based on available policy files.
1145                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
1146                 if (decHandler != null) {
1147                 // We've found the required interface policy files
1148                         RequiresDecl reqDecl = (RequiresDecl) decHandler.getRequiresDecl(intface);
1149                         Set<String> setExchInt = reqDecl.getInterfaces();
1150                         if (setExchInt.size() == 1) {
1151                                 Iterator iter = setExchInt.iterator();
1152                                 return (String) iter.next();
1153                         } else {
1154                                 throw new Error("IoTCompiler: Ambiguous stub interfaces: " + setExchInt.toString() + 
1155                                         ". Only one new interface can be declared if the object " + intface +
1156                                         " needs to be passed in as an input parameter!");
1157                         }
1158                 } else {
1159                 // NULL value - this means policy files missing
1160                         throw new Error("IoTCompiler: Parameter type lookup failed for " + intface +
1161                                 "... Please provide the necessary policy files for user-defined types." +
1162                                 " If this is an array please type the brackets after the variable name," +
1163                                 " e.g. \"String str[]\", not \"String[] str\"." +
1164                                 " If this is a Collections (Java) / STL (C++) type, this compiler only" +
1165                                 " supports List/ArrayList (Java) or list (C++).");
1166                 }
1167         }
1168
1169
1170         public static void main(String[] args) throws Exception {
1171
1172                 // If there is no argument or just "--help" or "-h", then invoke printUsage()
1173                 if ((args[0].equals("-help") ||
1174                          args[0].equals("--help")||
1175                          args[0].equals("-h"))   ||
1176                         (args.length == 0)) {
1177
1178                         IoTCompiler.printUsage();
1179
1180                 } else if (args.length > 1) {
1181
1182                         IoTCompiler comp = new IoTCompiler();
1183                         int i = 0;                              
1184                         do {
1185                                 // Parse main policy file
1186                                 ParseNode pnPol = IoTCompiler.parseFile(args[i]);
1187                                 // Parse "requires" policy file
1188                                 ParseNode pnReq = IoTCompiler.parseFile(args[i+1]);
1189                                 // Get interface name
1190                                 String intface = ParseTreeHandler.getOrigIntface(pnPol);
1191                                 comp.setDataStructures(intface, pnPol, pnReq);
1192                                 comp.getMethodsForIntface(intface);
1193                                 i = i + 2;
1194                         // 1) Check if this is the last option before "-java" or "-cplus"
1195                         // 2) Check if this is really the last option (no "-java" or "-cplus")
1196                         } while(!args[i].equals("-java") &&
1197                                         !args[i].equals("-cplus") &&
1198                                         (i < args.length));
1199
1200                         // Generate everything if we don't see "-java" or "-cplus"
1201                         if (i == args.length) {
1202                                 comp.generateJavaLocalInterfaces();
1203                                 comp.generateJavaInterfaces();
1204                                 comp.generateJavaStubClasses();
1205                                 comp.generateCplusLocalInterfaces();
1206                                 comp.generateCPlusInterfaces();
1207                                 comp.generateCPlusStubClasses();
1208                         } else {
1209                         // Check other options
1210                                 while(i < args.length) {
1211                                         // Error checking
1212                                         if (!args[i].equals("-java") &&
1213                                                 !args[i].equals("-cplus")) {
1214                                                 throw new Error("IoTCompiler: ERROR - unrecognized command line option: " + args[i]);
1215                                         } else {
1216                                                 if (i + 1 < args.length) {
1217                                                         comp.setDirectory(args[i+1]);
1218                                                 } else
1219                                                         throw new Error("IoTCompiler: ERROR - please provide <directory> after option: " + args[i]);
1220
1221                                                 if (args[i].equals("-java")) {
1222                                                         comp.generateJavaLocalInterfaces();
1223                                                         comp.generateJavaInterfaces();
1224                                                         comp.generateJavaStubClasses();
1225                                                 } else {
1226                                                         comp.generateCplusLocalInterfaces();
1227                                                         comp.generateCPlusInterfaces();
1228                                                         comp.generateCPlusStubClasses();
1229                                                 }
1230                                         }
1231                                         i = i + 2;
1232                                 }
1233                         }
1234                 } else {
1235                 // Need to at least have exactly 2 parameters, i.e. main policy file and requires file
1236                         IoTCompiler.printUsage();
1237                         throw new Error("IoTCompiler: At least two arguments (main and requires policy files) have to be provided!");
1238                 }
1239         }
1240 }
1241
1242
1243
1244