94029cad39c9e16d5040fef0578123b142466d52
[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                         print(getSimpleType(methPrmTypes.get(i)) + ".class");
306                         // Check if this is the last element (don't print a comma)
307                         if (i != methParams.size() - 1) {
308                                 print(", ");
309                         }
310                 }
311                 println(" };");
312                 // Generate array of parameter objects
313                 print("Object[] paramObj = new Object[] { ");
314                 for (int i = 0; i < methParams.size(); i++) {
315                         print(getSimpleIdentifier(methParams.get(i)));
316                         // Check if this is the last element (don't print a comma)
317                         if (i != methParams.size() - 1) {
318                                 print(", ");
319                         }
320                 }
321                 println(" };");
322                 // Check if this is "void"
323                 if (retType.equals("void")) {
324                         println("rmiCall.remoteCall(objectId, methodId, retType, null, paramCls, paramObj);");
325                 } else { // We do have a return value
326                 // Check if the return value NONPRIMITIVES
327                         if (getParamCategory(retType) == ParamCategory.NONPRIMITIVES) {
328                                 String[] retGenValType = getTypeOfGeneric(retType);
329                                 println("Class<?> retGenValType = " + retGenValType[0] + ".class;");
330                                 println("Object retObj = rmiCall.remoteCall(objectId, methodId, retType, retGenValType, paramCls, paramObj);");
331                                 println("return (" + retType + ")retObj;");
332                         } else {
333                                 println("Object retObj = rmiCall.remoteCall(objectId, methodId, retType, null, paramCls, paramObj);");
334                                 println("return (" + retType + ")retObj;");
335                         }
336                 }
337         }
338
339
340         /**
341          * HELPER: writeMethodJavaStub() writes the method of the stub class
342          */
343         private void writeMethodJavaStub(Collection<String> methods, InterfaceDecl intDecl) {
344
345                 for (String method : methods) {
346
347                         List<String> methParams = intDecl.getMethodParams(method);
348                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
349                         print("public " + intDecl.getMethodType(method) + " " +
350                                 intDecl.getMethodId(method) + "(");
351                         for (int i = 0; i < methParams.size(); i++) {
352
353                                 print(methPrmTypes.get(i) + " " + methParams.get(i));
354                                 // Check if this is the last element (don't print a comma)
355                                 if (i != methParams.size() - 1) {
356                                         print(", ");
357                                 }
358                         }
359                         println(") {");
360                         // Now, write the body of stub!
361                         writeStdMethodBodyJavaStub(intDecl, methParams, methPrmTypes, method);
362                         println("}\n");
363                 }
364         }
365
366
367         /**
368          * generateJavaStubClasses() generate stubs based on the methods list in Java
369          */
370         public void generateJavaStubClasses() throws IOException {
371
372                 // Create a new directory
373                 String path = createDirectories(dir, subdir);
374                 for (String intface : mapIntfacePTH.keySet()) {
375
376                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
377                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
378
379                                 // Open a new file to write into
380                                 String newIntface = intMeth.getKey();
381                                 String newStubClass = newIntface + "_Stub";
382                                 FileWriter fw = new FileWriter(path + "/" + newStubClass + ".java");
383                                 pw = new PrintWriter(new BufferedWriter(fw));
384                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
385                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
386                                 // Pass in set of methods and get import classes
387                                 Set<String> importClasses = getImportClasses(intMeth.getValue(), intDecl);
388                                 List<String> stdImportClasses = getStandardJavaImportClasses();
389                                 List<String> allImportClasses = getAllImportClasses(stdImportClasses, importClasses);
390                                 printImportStatements(allImportClasses); println("");
391                                 // Write interface header
392                                 println("public class " + newStubClass + " implements " + newIntface + " {\n");
393                                 // Write properties
394                                 writePropertiesJavaStub(intface);
395                                 // Write constructor
396                                 writeConstructorJavaStub(newStubClass);
397                                 // Write methods
398                                 writeMethodJavaStub(intMeth.getValue(), intDecl);
399                                 println("}");
400                                 pw.close();
401                                 System.out.println("IoTCompiler: Generated stub class " + newStubClass + ".java...");
402                         }
403                 }
404         }
405
406
407         /**
408          * HELPER: writeMethodCplusInterface() writes the method of the interface
409          */
410         private void writeMethodCplusInterface(Collection<String> methods, InterfaceDecl intDecl) {
411
412                 for (String method : methods) {
413
414                         List<String> methParams = intDecl.getMethodParams(method);
415                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
416                         print("virtual " + checkAndGetCplusType(intDecl.getMethodType(method)) + " " +
417                                 intDecl.getMethodId(method) + "(");
418                         for (int i = 0; i < methParams.size(); i++) {
419                                 // Check for params with driver class types and exchange it 
420                                 //              with its remote interface
421                                 String paramType = checkAndGetParamClass(methPrmTypes.get(i), true);
422                                 paramType = checkAndGetCplusType(paramType);
423                                 // Check for arrays - translate into vector in C++
424                                 String paramComplete = checkAndGetCplusArray(paramType, methParams.get(i));
425                                 print(paramComplete);
426                                 // Check if this is the last element (don't print a comma)
427                                 if (i != methParams.size() - 1) {
428                                         print(", ");
429                                 }
430                         }
431                         println(") = 0;");
432                 }
433         }
434
435
436         /**
437          * generateCplusLocalInterfaces() writes the local interfaces and provides type-checking.
438          * <p>
439          * It needs to rewrite and exchange USERDEFINED types in input parameters of stub
440          * and original interfaces, e.g. exchange Camera and CameraWithVideoAndRecording.
441          * The local interface has to be the input parameter for the stub and the stub 
442          * interface has to be the input parameter for the local class.
443          */
444         public void generateCplusLocalInterfaces() throws IOException {
445
446                 // Create a new directory
447                 createDirectory(dir);
448                 for (String intface : mapIntfacePTH.keySet()) {
449                         // Open a new file to write into
450                         FileWriter fw = new FileWriter(dir + "/" + intface + ".hpp");
451                         pw = new PrintWriter(new BufferedWriter(fw));
452                         // Write file headers
453                         println("#ifndef _" + intface.toUpperCase() + "_HPP__");
454                         println("#define _" + intface.toUpperCase() + "_HPP__");
455                         println("#include <iostream>");
456                         // Pass in set of methods and get include classes
457                         DeclarationHandler decHandler = mapIntDeclHand.get(intface);
458                         InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
459                         List<String> methods = intDecl.getMethods();
460                         Set<String> includeClasses = getIncludeClasses(methods, intDecl);
461                         printIncludeStatements(includeClasses); println("");
462                         println("using namespace std;\n");
463                         println("class " + intface); println("{");
464                         println("public:");
465                         // Write methods
466                         writeMethodCplusInterface(methods, intDecl);
467                         println("};");
468                         println("#endif");
469                         pw.close();
470                         System.out.println("IoTCompiler: Generated local interface " + intface + ".hpp...");
471                 }
472         }
473
474
475         /**
476          * generateCPlusInterfaces() generate stub interfaces based on the methods list in C++
477          * <p>
478          * For C++ we use virtual classe as interface
479          */
480         public void generateCPlusInterfaces() throws IOException {
481
482                 // Create a new directory
483                 String path = createDirectories(dir, subdir);
484                 for (String intface : mapIntfacePTH.keySet()) {
485
486                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
487                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
488
489                                 // Open a new file to write into
490                                 String newIntface = intMeth.getKey();
491                                 FileWriter fw = new FileWriter(path + "/" + newIntface + ".hpp");
492                                 pw = new PrintWriter(new BufferedWriter(fw));
493                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
494                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
495                                 // Write file headers
496                                 println("#ifndef _" + newIntface.toUpperCase() + "_HPP__");
497                                 println("#define _" + newIntface.toUpperCase() + "_HPP__");
498                                 println("#include <iostream>");
499                                 // Pass in set of methods and get import classes
500                                 Set<String> includeClasses = getIncludeClasses(intMeth.getValue(), intDecl);
501                                 List<String> stdIncludeClasses = getStandardCplusIncludeClasses();
502                                 List<String> allIncludeClasses = getAllImportClasses(stdIncludeClasses, includeClasses);
503                                 printIncludeStatements(allIncludeClasses); println("");                 
504                                 println("using namespace std;\n");
505                                 println("class " + newIntface);
506                                 println("{");
507                                 println("public:");
508                                 // Write methods
509                                 writeMethodCplusInterface(intMeth.getValue(), intDecl);
510                                 println("};");
511                                 println("#endif");
512                                 pw.close();
513                                 System.out.println("IoTCompiler: Generated interface " + newIntface + ".hpp...");
514                         }
515                 }
516         }
517
518
519         /**
520          * HELPER: writeMethodCplusStub() writes the method of the stub
521          */
522         private void writeMethodCplusStub(Collection<String> methods, InterfaceDecl intDecl) {
523
524                 for (String method : methods) {
525
526                         List<String> methParams = intDecl.getMethodParams(method);
527                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
528                         print(checkAndGetCplusType(intDecl.getMethodType(method)) + " " +
529                                 intDecl.getMethodId(method) + "(");
530                         for (int i = 0; i < methParams.size(); i++) {
531                                 String methPrmType = checkAndGetCplusType(methPrmTypes.get(i));
532                                 String methParamComplete = checkAndGetCplusArray(methPrmType, methParams.get(i));
533                                 print(methParamComplete);
534                                 // Check if this is the last element (don't print a comma)
535                                 if (i != methParams.size() - 1) {
536                                         print(", ");
537                                 }
538                         }
539                         println(") { ");
540                         writeStdMethodBodyCplusStub(intDecl, methParams, methPrmTypes, method);
541                         println("}\n");
542                 }
543         }
544
545
546         /**
547          * HELPER: writeStdMethodBodyCplusStub() writes the standard method body in the stub class
548          */
549         private void writeStdMethodBodyCplusStub(InterfaceDecl intDecl, List<String> methParams,
550                         List<String> methPrmTypes, String method) {
551
552                 println("int numParam = " + methParams.size() + ";");
553                 println("int methodId = " + intDecl.getMethodNumId(method) + ";");
554                 String retType = intDecl.getMethodType(method);
555                 println("string retType = \"" + checkAndGetCplusType(retType) + "\";");
556                 // Generate array of parameter types
557                 print("string paramCls[] = { ");
558                 for (int i = 0; i < methParams.size(); i++) {
559                         print("\"" + checkAndGetCplusType(methPrmTypes.get(i)) + "\"");
560                         // Check if this is the last element (don't print a comma)
561                         if (i != methParams.size() - 1) {
562                                 print(", ");
563                         }
564                 }
565                 println(" };");
566                 // Generate array of parameter objects
567                 print("void* paramObj[] = { ");
568                 for (int i = 0; i < methParams.size(); i++) {
569                         print("&" + checkAndGetCplusType(getSimpleIdentifier(methParams.get(i))));
570                         // Check if this is the last element (don't print a comma)
571                         if (i != methParams.size() - 1) {
572                                 print(", ");
573                         }
574                 }
575                 println(" };");
576                 // Check if this is "void"
577                 if (retType.equals("void")) {
578                         println("void* retObj = NULL;");
579                         println("rmiCall->remoteCall(objectId, methodId, retType, paramCls, paramObj, numParam, retObj);");
580                 } else { // We do have a return value
581                         if (getParamCategory(retType) == ParamCategory.NONPRIMITIVES)
582                                 println(checkAndGetCplusType(retType) + " retVal;");
583                         else
584                                 println(checkAndGetCplusType(retType) + " retVal = " + generateCplusInitializer(retType) + ";");
585                         println("void* retObj = &retVal;");
586                         println("rmiCall->remoteCall(objectId, methodId, retType, paramCls, paramObj, numParam, retObj);");
587                         println("return retVal;");
588                 }
589         }
590
591
592         /**
593          * HELPER: writePropertiesCplusStub() writes the properties of the stub class
594          */
595         private void writePropertiesCplusStub(String intface) {
596
597                 println("IoTRMICall\t\t\t*rmiCall;");
598                 //println("IoTRMIObject\t\t\t*rmiObj;");
599                 println("string\t\t\t\taddress;");
600                 println("vector<int>\t\t\tports;\n");
601                 // Get the object Id
602                 Integer objId = mapIntfaceObjId.get(intface);
603                 println("const static int\tobjectId = " + objId + ";");
604                 mapIntfaceObjId.put(intface, objId++);
605                 println("\n");
606         }
607
608
609         /**
610          * HELPER: writeConstructorCplusStub() writes the constructor of the stub class
611          */
612         private void writeConstructorCplusStub(String newStubClass) {
613
614                 println(newStubClass + 
615                         "(int _port, const char* _address, int _rev, bool* _bResult, vector<int> _ports) {");
616                 println("address = _address;");
617                 println("ports = _ports;");
618                 println("rmiCall = new IoTRMICall(_port, _address, _rev, _bResult);");
619                 println("}\n");
620         }
621
622
623         /**
624          * HELPER: writeDeconstructorCplusStub() writes the deconstructor of the stub class
625          */
626         private void writeDeconstructorCplusStub(String newStubClass) {
627
628                 println("~" + newStubClass + "() {");
629                 println("if (rmiCall != NULL) {");
630                 println("delete rmiCall;");
631                 println("rmiCall = NULL;");
632                 println("}");
633                 println("}");
634                 println("");
635                 // Check if this is callback!!! and print "delete rmiObj and vecCBObj"
636         }
637
638
639         /**
640          * generateCPlusStubClasses() generate stubs based on the methods list in C++
641          */
642         public void generateCPlusStubClasses() throws IOException {
643
644                 // Create a new directory
645                 String path = createDirectories(dir, subdir);
646                 for (String intface : mapIntfacePTH.keySet()) {
647
648                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
649                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
650                                 // Open a new file to write into
651                                 String newIntface = intMeth.getKey();
652                                 String newStubClass = newIntface + "_Stub";
653                                 FileWriter fw = new FileWriter(path + "/" + newStubClass + ".hpp");
654                                 pw = new PrintWriter(new BufferedWriter(fw));
655                                 // Write file headers
656                                 println("#ifndef _" + newStubClass.toUpperCase() + "_HPP__");
657                                 println("#define _" + newStubClass.toUpperCase() + "_HPP__");
658                                 println("#include <iostream>");
659                                 println("#include \"" + newIntface + ".hpp\""); println("");            
660                                 println("using namespace std;"); println("");
661                                 println("class " + newStubClass + " : public " + newIntface); println("{");
662                                 println("private:\n");
663                                 writePropertiesCplusStub(intface);
664                                 println("public:\n");
665                                 // Add default constructor and destructor
666                                 println(newStubClass + "() { }"); println("");
667                                 writeConstructorCplusStub(newStubClass);
668                                 writeDeconstructorCplusStub(newStubClass);
669                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
670                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
671                                 // Write methods
672                                 writeMethodCplusStub(intMeth.getValue(), intDecl);
673                                 print("}"); println(";");
674                                 println("#endif");
675                                 pw.close();
676                                 System.out.println("IoTCompiler: Generated stub class " + newIntface + ".hpp...");
677                         }
678                 }
679         }
680
681
682         /**
683          * generateInitializer() generate initializer based on type
684          */
685         public String generateCplusInitializer(String type) {
686
687                 // Generate dummy returns for now
688                 if (type.equals("short")||
689                         type.equals("int")      ||
690                         type.equals("long") ||
691                         type.equals("float")||
692                         type.equals("double")) {
693
694                         return "0";
695                 } else if ( type.equals("String") ||
696                                         type.equals("string")) {
697   
698                         return "\"\"";
699                 } else if ( type.equals("char") ||
700                                         type.equals("byte")) {
701
702                         return "\' \'";
703                 } else if ( type.equals("boolean")) {
704
705                         return "false";
706                 } else {
707                         return "NULL";
708                 }
709         }
710
711
712         /**
713          * generateReturnStmt() generate return statement based on methType
714          */
715         public String generateReturnStmt(String methType) {
716
717                 // Generate dummy returns for now
718                 if (methType.equals("short")||
719                         methType.equals("int")  ||
720                         methType.equals("long") ||
721                         methType.equals("float")||
722                         methType.equals("double")) {
723
724                         return "1";
725                 } else if ( methType.equals("String")) {
726   
727                         return "\"a\"";
728                 } else if ( methType.equals("char") ||
729                                         methType.equals("byte")) {
730
731                         return "\'a\'";
732                 } else if ( methType.equals("boolean")) {
733
734                         return "true";
735                 } else {
736                         return "null";
737                 }
738         }
739
740
741         /**
742          * setDirectory() sets a new directory for stub files
743          */
744         public void setDirectory(String _subdir) {
745
746                 subdir = _subdir;
747         }
748
749
750         /**
751          * printUsage() prints the usage of this compiler
752          */
753         public static void printUsage() {
754
755                 System.out.println();
756                 System.out.println("Sentinel interface and stub compiler version 1.0");
757                 System.out.println("Copyright (c) 2015-2016 University of California, Irvine - Programming Language Group.");
758                 System.out.println("All rights reserved.");
759                 System.out.println("Usage:");
760                 System.out.println("\tjava IoTCompiler -help / --help / -h\n");
761                 System.out.println("\t\tDisplay this help texts\n\n");
762                 System.out.println("\tjava IoTCompiler [<main-policy-file> <req-policy-file>]");
763                 System.out.println("\tjava IoTCompiler [<main-policy-file> <req-policy-file>] [options]\n");
764                 System.out.println("\t\tTake one or more pairs of main-req policy files, and generate Java and/or C++ files\n");
765                 System.out.println("Options:");
766                 System.out.println("\t-java\t<directory>\tGenerate Java stub files");
767                 System.out.println("\t-cplus\t<directory>\tGenerate C++ stub files");
768                 System.out.println();
769         }
770
771
772         /**
773          * parseFile() prepares Lexer and Parser objects, then parses the file
774          */
775         public static ParseNode parseFile(String file) {
776
777                 ParseNode pn = null;
778                 try {
779                         ComplexSymbolFactory csf = new ComplexSymbolFactory();
780                         ScannerBuffer lexer = 
781                                 new ScannerBuffer(new Lexer(new BufferedReader(new FileReader(file)),csf));
782                         Parser parse = new Parser(lexer,csf);
783                         pn = (ParseNode) parse.parse().value;
784                 } catch (Exception e) {
785                         e.printStackTrace();
786                         throw new Error("IoTCompiler: ERROR parsing policy file or wrong command line option: " + file);
787                 }
788
789                 return pn;
790         }
791
792
793         /**================
794          * Helper functions
795          **================
796          */
797         boolean newline=true;
798         int tablevel=0;
799
800         private void print(String str) {
801                 if (newline) {
802                         int tab=tablevel;
803                         if (str.equals("}"))
804                                 tab--;
805                         for(int i=0; i<tab; i++)
806                                 pw.print("\t");
807                 }
808                 pw.print(str);
809                 updatetabbing(str);
810                 newline=false;
811         }
812
813
814         /**
815          * This function converts Java to C++ type for compilation
816          */
817         private String convertType(String jType) {
818
819                 return mapPrimitives.get(jType);
820         }
821
822
823         private void println(String str) {
824                 if (newline) {
825                         int tab = tablevel;
826                         if (str.equals("}"))
827                                 tab--;
828                         for(int i=0; i<tab; i++)
829                                 pw.print("\t");
830                 }
831                 pw.println(str);
832                 updatetabbing(str);
833                 newline = true;
834         }
835
836
837         private void updatetabbing(String str) {
838                 tablevel+=count(str,'{')-count(str,'}');
839         }
840
841
842         private int count(String str, char key) {
843                 char[] array = str.toCharArray();
844                 int count = 0;
845                 for(int i=0; i<array.length; i++) {
846                         if (array[i] == key)
847                                 count++;
848                 }
849                 return count;
850         }
851
852
853         private void createDirectory(String dirName) {
854
855                 File file = new File(dirName);
856                 if (!file.exists()) {
857                         if (file.mkdir()) {
858                                 System.out.println("IoTCompiler: Directory " + dirName + " has been created!");
859                         } else {
860                                 System.out.println("IoTCompiler: Failed to create directory " + dirName + "!");
861                         }
862                 } else {
863                         System.out.println("IoTCompiler: Directory " + dirName + " exists...");
864                 }
865         }
866
867
868         // Create a directory and possibly a sub directory
869         private String createDirectories(String dir, String subdir) {
870
871                 String path = dir;
872                 createDirectory(path);
873                 if (subdir != null) {
874                         path = path + "/" + subdir;
875                         createDirectory(path);
876                 }
877                 return path;
878         }
879
880
881         // Inserting array members into a Map object
882         // that maps arrKey to arrVal objects
883         private void arraysToMap(Map map, Object[] arrKey, Object[] arrVal) {
884
885                 for(int i = 0; i < arrKey.length; i++) {
886
887                         map.put(arrKey[i], arrVal[i]);
888                 }
889         }
890
891
892         // Return parameter category, i.e. PRIMITIVES, NONPRIMITIVES, or USERDEFINED
893         private ParamCategory getParamCategory(String paramType) {
894
895                 if (mapPrimitives.containsKey(paramType)) {
896                         return ParamCategory.PRIMITIVES;
897                 // We can either use mapNonPrimitivesJava or mapNonPrimitivesCplus here
898                 } else if (mapNonPrimitivesJava.containsKey(getSimpleType(paramType))) {
899                         return ParamCategory.NONPRIMITIVES;
900                 } else
901                         return ParamCategory.USERDEFINED;
902         }
903
904
905         // Return full class name for non-primitives to generate Java import statements
906         // e.g. java.util.Set for Set, java.util.Map for Map
907         private String getNonPrimitiveJavaClass(String paramNonPrimitives) {
908
909                 return mapNonPrimitivesJava.get(paramNonPrimitives);
910         }
911
912
913         // Return full class name for non-primitives to generate Cplus include statements
914         // e.g. #include <set> for Set, #include <map> for Map
915         private String getNonPrimitiveCplusClass(String paramNonPrimitives) {
916
917                 return mapNonPrimitivesCplus.get(paramNonPrimitives);
918         }
919
920
921         // Get simple types, e.g. HashSet for HashSet<...>
922         // Basically strip off the "<...>"
923         private String getSimpleType(String paramType) {
924
925                 // Check if this is generics
926                 if(paramType.contains("<")) {
927                         String[] type = paramType.split("<");
928                         return type[0];
929                 } else
930                         return paramType;
931         }
932
933
934         // Generate a set of standard classes for import statements
935         private List<String> getStandardJavaImportClasses() {
936
937                 List<String> importClasses = new ArrayList<String>();
938                 // Add the standard list first
939                 importClasses.add("java.io.IOException");
940                 importClasses.add("java.util.List");
941                 importClasses.add("java.util.ArrayList");
942                 importClasses.add("iotrmi.Java.IoTRMICall");
943                 importClasses.add("iotrmi.Java.IoTRMIObject");
944
945                 return importClasses;
946         }
947
948
949         // Generate a set of standard classes for import statements
950         private List<String> getStandardCplusIncludeClasses() {
951
952                 List<String> importClasses = new ArrayList<String>();
953                 // Add the standard list first
954                 importClasses.add("<vector>");
955                 importClasses.add("\"IoTRMICall.hpp\"");
956                 importClasses.add("\"IoTRMIObject.hpp\"");
957
958                 return importClasses;
959         }
960
961
962         // Generate a set of standard classes for import statements
963         private List<String> getAllImportClasses(Collection<String> stdImportClasses, Collection<String> importClasses) {
964
965                 List<String> allImportClasses = new ArrayList<String>(stdImportClasses);
966                 // Iterate over the list of import classes
967                 for (String str : importClasses) {
968                         if (!stdImportClasses.contains(str)) {
969                                 stdImportClasses.add(str);
970                         }
971                 }
972
973                 return allImportClasses;
974         }
975
976
977
978         // Generate a set of classes for import statements
979         private Set<String> getImportClasses(Collection<String> methods, InterfaceDecl intDecl) {
980
981                 Set<String> importClasses = new HashSet<String>();
982                 for (String method : methods) {
983                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
984                         for (String paramType : methPrmTypes) {
985
986                                 String simpleType = getSimpleType(paramType);
987                                 if (getParamCategory(simpleType) == ParamCategory.NONPRIMITIVES) {
988                                         importClasses.add(getNonPrimitiveJavaClass(simpleType));
989                                 }
990                         }
991                 }
992                 return importClasses;
993         }
994
995
996         // Generate a set of classes for include statements
997         private Set<String> getIncludeClasses(Collection<String> methods, InterfaceDecl intDecl) {
998
999                 Set<String> includeClasses = new HashSet<String>();
1000                 for (String method : methods) {
1001
1002                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
1003                         List<String> methParams = intDecl.getMethodParams(method);
1004                         for (int i = 0; i < methPrmTypes.size(); i++) {
1005
1006                                 String simpleType = getSimpleType(methPrmTypes.get(i));
1007                                 String param = methParams.get(i);
1008                                 if (getParamCategory(simpleType) == ParamCategory.NONPRIMITIVES) {
1009                                         includeClasses.add("<" + getNonPrimitiveCplusClass(simpleType) + ">");
1010                                 } else if (getParamCategory(simpleType) == ParamCategory.USERDEFINED) {
1011                                         includeClasses.add("\"" + exchangeParamType(simpleType) + ".hpp\"");
1012                                 } else if (param.contains("[]")) {
1013                                 // Check if this is array for C++; translate into vector
1014                                         includeClasses.add("<vector>");
1015                                 }
1016                         }
1017                 }
1018                 return includeClasses;
1019         }
1020
1021
1022         private void printImportStatements(Collection<String> importClasses) {
1023
1024                 for(String cls : importClasses) {
1025                         println("import " + cls + ";");
1026                 }
1027         }
1028
1029
1030         private void printIncludeStatements(Collection<String> includeClasses) {
1031
1032                 for(String cls : includeClasses) {
1033                         println("#include " + cls);
1034                 }
1035         }
1036
1037
1038         // Get the C++ version of a non-primitive type
1039         // e.g. set for Set and map for Map
1040         // Input nonPrimitiveType has to be generics in format
1041         private String[] getTypeOfGeneric(String nonPrimitiveType) {
1042
1043                 // Handle <, >, and , for 2-type generic/template
1044                 String[] substr = nonPrimitiveType.split("<")[1].split(">")[0].split(",");
1045                 return substr;
1046         }
1047
1048
1049         // This helper function strips off array declaration, e.g. D[] becomes D
1050         private String getSimpleIdentifier(String ident) {
1051
1052                 // Handle [ for array declaration
1053                 String substr = ident;
1054                 if (ident.contains("[]")) {
1055                         substr = ident.split("\\[\\]")[0];
1056                 }
1057                 return substr;
1058         }
1059
1060
1061         private String checkAndGetCplusType(String paramType) {
1062
1063                 if (getParamCategory(paramType) == ParamCategory.PRIMITIVES) {
1064                         return convertType(paramType);
1065                 } else if (getParamCategory(paramType) == ParamCategory.NONPRIMITIVES) {
1066
1067                         // Check for generic/template format
1068                         if (paramType.contains("<") && paramType.contains(">")) {
1069
1070                                 String genericClass = getSimpleType(paramType);
1071                                 String[] genericType = getTypeOfGeneric(paramType);
1072                                 String cplusTemplate = null;
1073                                 if (genericType.length == 1) // Generic/template with one type
1074                                         cplusTemplate = getNonPrimitiveCplusClass(genericClass) + 
1075                                                 "<" + convertType(genericType[0]) + ">";
1076                                 else // Generic/template with two types
1077                                         cplusTemplate = getNonPrimitiveCplusClass(genericClass) + 
1078                                                 "<" + convertType(genericType[0]) + "," + convertType(genericType[1]) + ">";
1079                                 return cplusTemplate;
1080                         } else
1081                                 return getNonPrimitiveCplusClass(paramType);
1082                 } else
1083                         // Just return it as is if it's not non-primitives
1084                         return paramType;
1085         }
1086
1087
1088         // Detect array declaration, e.g. int A[],
1089         //              then generate "int A[]" in C++ as "vector<int> A"
1090         private String checkAndGetCplusArray(String paramType, String param) {
1091
1092                 String paramComplete = null;
1093                 // Check for array declaration
1094                 if (param.contains("[]")) {
1095                         paramComplete = "vector<" + paramType + "> " + param.replace("[]","");
1096                 } else
1097                         // Just return it as is if it's not an array
1098                         paramComplete = paramType + " " + param;
1099
1100                 return paramComplete;
1101         }
1102
1103
1104         // Get simple types, e.g. HashSet for HashSet<...>
1105         // Basically strip off the "<...>"
1106         private String checkAndGetParamClass(String paramType, boolean needPtr) {
1107
1108                 // Check if this is generics
1109                 if(getParamCategory(paramType) == ParamCategory.USERDEFINED) {
1110                         // If true then return with pointer (C++)
1111                         if (needPtr)
1112                                 return exchangeParamType(paramType) + "*";
1113                         else    // Java, so no pointer needed
1114                                 return exchangeParamType(paramType);
1115                 } else
1116                         return paramType;
1117         }
1118
1119
1120         // Returns the other interface for type-checking purposes for USERDEFINED
1121         //              classes based on the information provided in multiple policy files
1122         // e.g. return CameraWithXXX instead of Camera
1123         private String exchangeParamType(String intface) {
1124
1125                 // Param type that's passed is the interface name we need to look for
1126                 //              in the map of interfaces, based on available policy files.
1127                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
1128                 if (decHandler != null) {
1129                 // We've found the required interface policy files
1130                         RequiresDecl reqDecl = (RequiresDecl) decHandler.getRequiresDecl(intface);
1131                         Set<String> setExchInt = reqDecl.getInterfaces();
1132                         if (setExchInt.size() == 1) {
1133                                 Iterator iter = setExchInt.iterator();
1134                                 return (String) iter.next();
1135                         } else {
1136                                 throw new Error("IoTCompiler: Ambiguous stub interfaces: " + setExchInt.toString() + 
1137                                         ". Only one new interface can be declared if the object " + intface +
1138                                         " needs to be passed in as an input parameter!");
1139                         }
1140                 } else {
1141                 // NULL value - this means policy files missing
1142                         throw new Error("IoTCompiler: Parameter type lookup failed for " + intface +
1143                                 "... Please provide the necessary policy files for user-defined types." +
1144                                 " If this is an array please type the brackets after the variable name," +
1145                                 " e.g. \"String str[]\", not \"String[] str\"." +
1146                                 " If this is a Collections (Java) / STL (C++) type, this compiler only" +
1147                                 " supports List/ArrayList (Java) or list (C++).");
1148                 }
1149         }
1150
1151
1152         public static void main(String[] args) throws Exception {
1153
1154                 // If there is no argument or just "--help" or "-h", then invoke printUsage()
1155                 if ((args[0].equals("-help") ||
1156                          args[0].equals("--help")||
1157                          args[0].equals("-h"))   ||
1158                         (args.length == 0)) {
1159
1160                         IoTCompiler.printUsage();
1161
1162                 } else if (args.length > 1) {
1163
1164                         IoTCompiler comp = new IoTCompiler();
1165                         int i = 0;                              
1166                         do {
1167                                 // Parse main policy file
1168                                 ParseNode pnPol = IoTCompiler.parseFile(args[i]);
1169                                 // Parse "requires" policy file
1170                                 ParseNode pnReq = IoTCompiler.parseFile(args[i+1]);
1171                                 // Get interface name
1172                                 String intface = ParseTreeHandler.getOrigIntface(pnPol);
1173                                 comp.setDataStructures(intface, pnPol, pnReq);
1174                                 comp.getMethodsForIntface(intface);
1175                                 i = i + 2;
1176                         // 1) Check if this is the last option before "-java" or "-cplus"
1177                         // 2) Check if this is really the last option (no "-java" or "-cplus")
1178                         } while(!args[i].equals("-java") &&
1179                                         !args[i].equals("-cplus") &&
1180                                         (i < args.length));
1181
1182                         // Generate everything if we don't see "-java" or "-cplus"
1183                         if (i == args.length) {
1184                                 comp.generateJavaLocalInterfaces();
1185                                 comp.generateJavaInterfaces();
1186                                 comp.generateJavaStubClasses();
1187                                 comp.generateCplusLocalInterfaces();
1188                                 comp.generateCPlusInterfaces();
1189                                 comp.generateCPlusStubClasses();
1190                         } else {
1191                         // Check other options
1192                                 while(i < args.length) {
1193                                         // Error checking
1194                                         if (!args[i].equals("-java") &&
1195                                                 !args[i].equals("-cplus")) {
1196                                                 throw new Error("IoTCompiler: ERROR - unrecognized command line option: " + args[i]);
1197                                         } else {
1198                                                 if (i + 1 < args.length) {
1199                                                         comp.setDirectory(args[i+1]);
1200                                                 } else
1201                                                         throw new Error("IoTCompiler: ERROR - please provide <directory> after option: " + args[i]);
1202
1203                                                 if (args[i].equals("-java")) {
1204                                                         comp.generateJavaLocalInterfaces();
1205                                                         comp.generateJavaInterfaces();
1206                                                         comp.generateJavaStubClasses();
1207                                                 } else {
1208                                                         comp.generateCplusLocalInterfaces();
1209                                                         comp.generateCPlusInterfaces();
1210                                                         comp.generateCPlusStubClasses();
1211                                                 }
1212                                         }
1213                                         i = i + 2;
1214                                 }
1215                         }
1216                 } else {
1217                 // Need to at least have exactly 2 parameters, i.e. main policy file and requires file
1218                         IoTCompiler.printUsage();
1219                         throw new Error("IoTCompiler: At least two arguments (main and requires policy files) have to be provided!");
1220                 }
1221         }
1222 }
1223
1224
1225
1226