4450f1d3c74a7e39b0a00069a92c158e9bb266cc
[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.Collection;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14
15 import iotpolicy.parser.Lexer;
16 import iotpolicy.parser.Parser;
17 import iotpolicy.tree.ParseNode;
18 import iotpolicy.tree.ParseNodeVector;
19 import iotpolicy.tree.ParseTreeHandler;
20 import iotpolicy.tree.Declaration;
21 import iotpolicy.tree.DeclarationHandler;
22 import iotpolicy.tree.CapabilityDecl;
23 import iotpolicy.tree.InterfaceDecl;
24 import iotpolicy.tree.RequiresDecl;
25
26 import iotrmi.Java.IoTRMITypes;
27
28
29 /** Class IoTCompiler is the main interface/stub compiler for
30  *  files generation. This class calls helper classes
31  *  such as Parser, Lexer, InterfaceDecl, CapabilityDecl,
32  *  RequiresDecl, ParseTreeHandler, etc.
33  *
34  * @author      Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
35  * @version     1.0
36  * @since       2016-09-22
37  */
38 public class IoTCompiler {
39
40         /**
41          * Class properties
42          */
43         // Maps multiple interfaces to multiple objects of ParseTreeHandler
44         private Map<String,ParseTreeHandler> mapIntfacePTH;
45         private Map<String,DeclarationHandler> mapIntDeclHand;
46         private Map<String,Map<String,Set<String>>> mapInt2NewInts;
47         // Data structure to store our types (primitives and non-primitives) for compilation
48         private Map<String,String> mapPrimitives;
49         private Map<String,String> mapNonPrimitivesJava;
50         private Map<String,String> mapNonPrimitivesCplus;
51         private PrintWriter pw;
52         private String dir;
53         private String subdir;
54
55         /**
56          * Class constants
57          */
58         private final static String OUTPUT_DIRECTORY = "output_files";
59
60         private enum ParamCategory {
61
62                 PRIMITIVES,             // All the primitive types, e.g. byte, short, int, long, etc.
63                 NONPRIMITIVES,  // Non-primitive types, e.g. Set, Map, List, etc.
64                 USERDEFINED             // Non-supported type by default; assumed as driver classes
65         }
66
67         /**
68          * Class constructors
69          */
70         public IoTCompiler() {
71
72                 mapIntfacePTH = new HashMap<String,ParseTreeHandler>();
73                 mapIntDeclHand = new HashMap<String,DeclarationHandler>();
74                 mapInt2NewInts = new HashMap<String,Map<String,Set<String>>>();
75                 mapPrimitives = new HashMap<String,String>();
76                         arraysToMap(mapPrimitives, IoTRMITypes.primitivesJava, IoTRMITypes.primitivesCplus);
77                 mapNonPrimitivesJava = new HashMap<String,String>();
78                         arraysToMap(mapNonPrimitivesJava, IoTRMITypes.nonPrimitivesJava, IoTRMITypes.nonPrimitiveJavaLibs);
79                 mapNonPrimitivesCplus = new HashMap<String,String>();
80                         arraysToMap(mapNonPrimitivesCplus, IoTRMITypes.nonPrimitivesJava, IoTRMITypes.nonPrimitivesCplus);
81                 pw = null;
82                 dir = OUTPUT_DIRECTORY;
83                 subdir = null;
84         }
85
86
87         /**
88          * setParseTree() sets parse tree based on policy files.
89          * <p>
90          * It also generates parse tree (ParseTreeHandler) and
91          * copies useful information from parse tree into
92          * InterfaceDecl, CapabilityDecl, and RequiresDecl 
93          * data structures.
94          * Additionally, the data structure handles are
95          * returned from tree-parsing for further process.
96          *
97          */
98         public void setParseTree(String origInt, ParseNode pnPol, ParseNode pnReq) {
99
100                 ParseTreeHandler ptHandler = new ParseTreeHandler(origInt, pnPol, pnReq);
101                 DeclarationHandler decHandler = new DeclarationHandler();
102
103                 // Process ParseNode and generate Declaration objects
104                 ptHandler.processInterfaceDecl();
105                 InterfaceDecl intDecl = ptHandler.getInterfaceDecl();
106                 decHandler.addInterfaceDecl(origInt, intDecl);
107                 ptHandler.processCapabilityDecl();
108                 CapabilityDecl capDecl = ptHandler.getCapabilityDecl();
109                 decHandler.addCapabilityDecl(origInt, capDecl);
110                 ptHandler.processRequiresDecl();
111                 RequiresDecl reqDecl = ptHandler.getRequiresDecl();
112                 decHandler.addRequiresDecl(origInt, reqDecl);
113
114                 mapIntfacePTH.put(origInt, ptHandler);
115                 mapIntDeclHand.put(origInt, decHandler);
116         }
117
118
119         /**
120          * getMethodsForIntface() reads for methods in the data structure
121          * <p>
122          * It is going to give list of methods for a certain interface
123          *              based on the declaration of capabilities.
124          */
125         public void getMethodsForIntface(String origInt) {
126
127                 ParseTreeHandler ptHandler = mapIntfacePTH.get(origInt);
128                 Map<String,Set<String>> mapNewIntMethods = new HashMap<String,Set<String>>();
129                 // Get set of new interfaces, e.g. CameraWithCaptureAndData
130                 // Generate this new interface with all the methods it needs
131                 //              from different capabilities it declares
132                 DeclarationHandler decHandler = mapIntDeclHand.get(origInt);
133                 RequiresDecl reqDecl = (RequiresDecl) decHandler.getRequiresDecl(origInt);
134                 Set<String> setIntfaces = reqDecl.getInterfaces();
135                 for (String strInt : setIntfaces) {
136
137                         // Initialize a set of methods
138                         Set<String> setMethods = new HashSet<String>();
139                         // Get list of capabilities, e.g. ImageCapture, VideoRecording, etc.
140                         List<String> listCapab = reqDecl.getCapabList(strInt);
141                         for (String strCap : listCapab) {
142
143                                 // Get list of methods for each capability
144                                 CapabilityDecl capDecl = (CapabilityDecl) decHandler.getCapabilityDecl(origInt);
145                                 List<String> listCapabMeth = capDecl.getMethods(strCap);
146                                 for (String strMeth : listCapabMeth) {
147
148                                         // Add methods into setMethods
149                                         // This is to also handle redundancies (say two capabilities
150                                         //              share the same methods)
151                                         setMethods.add(strMeth);
152                                 }
153                         }
154                         // Add interface and methods information into map
155                         mapNewIntMethods.put(strInt, setMethods);
156                 }
157                 // Map the map of interface-methods to the original interface
158                 mapInt2NewInts.put(origInt, mapNewIntMethods);
159         }
160
161
162         /**
163          * generateJavaLocalInterface() writes the local interface and provides type-checking.
164          * <p>
165          * It needs to rewrite and exchange USERDEFINED types in input parameters of stub
166          * and original interfaces, e.g. exchange Camera and CameraWithVideoAndRecording.
167          * The local interface has to be the input parameter for the stub and the stub 
168          * interface has to be the input parameter for the local class.
169          */
170         public void generateJavaLocalInterfaces() throws IOException {
171
172                 // Create a new directory
173                 createDirectory(dir);
174                 for (String intface : mapIntfacePTH.keySet()) {
175                         // Open a new file to write into
176                         FileWriter fw = new FileWriter(dir + "/" + intface + ".java");
177                         pw = new PrintWriter(new BufferedWriter(fw));
178                         // Pass in set of methods and get import classes
179                         DeclarationHandler decHandler = mapIntDeclHand.get(intface);
180                         InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
181                         List<String> methods = intDecl.getMethods();
182                         Set<String> importClasses = getImportClasses(methods, intDecl);
183                         printImportStatements(importClasses);
184                         // Write interface header
185                         println("");
186                         println("public interface " + intface + " {");
187                         // Write methods
188                         for (String method : methods) {
189
190                                 List<String> methParams = intDecl.getMethodParams(method);
191                                 List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
192                                 print("public " + intDecl.getMethodType(method) + " " +
193                                         intDecl.getMethodId(method) + "(");
194                                 for (int i = 0; i < methParams.size(); i++) {
195                                         // Check for params with driver class types and exchange it 
196                                         //              with its remote interface
197                                         String paramType = checkAndGetParamClass(methPrmTypes.get(i), false);
198                                         print(paramType + " " + methParams.get(i));
199                                         // Check if this is the last element (don't print a comma)
200                                         if (i != methParams.size() - 1) {
201                                                 print(", ");
202                                         }
203                                 }
204                                 println(");");
205                         }
206                         println("}");
207                         pw.close();
208                         System.out.println("IoTCompiler: Generated local interface " + intface + ".java...");
209                 }
210         }
211
212
213         /**
214          * generateCplusLocalInterfaces() writes the local interfaces and provides type-checking.
215          * <p>
216          * It needs to rewrite and exchange USERDEFINED types in input parameters of stub
217          * and original interfaces, e.g. exchange Camera and CameraWithVideoAndRecording.
218          * The local interface has to be the input parameter for the stub and the stub 
219          * interface has to be the input parameter for the local class.
220          */
221         public void generateCplusLocalInterfaces() throws IOException {
222
223                 // Create a new directory
224                 createDirectory(dir);
225                 for (String intface : mapIntfacePTH.keySet()) {
226                         // Open a new file to write into
227                         FileWriter fw = new FileWriter(dir + "/" + intface + ".hpp");
228                         pw = new PrintWriter(new BufferedWriter(fw));
229                         // Write file headers
230                         println("#include <iostream>");
231                         // Pass in set of methods and get include classes
232                         DeclarationHandler decHandler = mapIntDeclHand.get(intface);
233                         InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
234                         List<String> methods = intDecl.getMethods();
235
236                         // DEBUGGG
237                         Set<String> includeClasses = getIncludeClasses(methods, intDecl);
238
239                         printIncludeStatements(includeClasses);
240                         println("");
241                         println("using namespace std;");
242                         println("");
243                         println("class " + intface);
244                         println("{");
245                         println("public:");
246                         // Write methods
247                         for (String method : methods) {
248
249                                 List<String> methParams = intDecl.getMethodParams(method);
250                                 List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
251                                 print("virtual " + convertType(intDecl.getMethodType(method)) + " " +
252                                         intDecl.getMethodId(method) + "(");
253                                 for (int i = 0; i < methParams.size(); i++) {
254                                         // Check for params with driver class types and exchange it 
255                                         //              with its remote interface
256                                         String paramType = checkAndGetParamClass(methPrmTypes.get(i), true);
257                                         paramType = checkAndGetCplusType(paramType);
258                                         // Check for arrays - translate into vector in C++
259                                         String paramComplete = checkAndGetCplusArray(paramType, methParams.get(i));
260                                         //print(paramType + " " + param);
261                                         print(paramComplete);
262                                         // Check if this is the last element (don't print a comma)
263                                         if (i != methParams.size() - 1) {
264                                                 print(", ");
265                                         }
266                                 }
267                                 println(") = 0;");
268                         }
269                         print("}");
270                         println(";");
271                         pw.close();
272                         System.out.println("IoTCompiler: Generated local interface " + intface + ".hpp...");
273                 }
274         }
275
276
277         /**
278          * generateJavaInterfaces() generate stub interfaces based on the methods list in Java
279          */
280         public void generateJavaInterfaces() throws IOException {
281
282                 // Create a new directory
283                 String path = createDirectories(dir, subdir);
284                 for (String intface : mapIntfacePTH.keySet()) {
285
286                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
287                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
288
289                                 // Open a new file to write into
290                                 String newIntface = intMeth.getKey();
291                                 FileWriter fw = new FileWriter(path + "/" + newIntface + ".java");
292                                 pw = new PrintWriter(new BufferedWriter(fw));
293                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
294                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
295                                 // Pass in set of methods and get import classes
296                                 Set<String> importClasses = getImportClasses(intMeth.getValue(), intDecl);
297                                 printImportStatements(importClasses);
298                                 // Write interface header
299                                 println("");
300                                 println("public interface " + newIntface + " {");
301                                 List<String> meths = intDecl.getMethods();
302                                 // Write methods
303                                 for (String method : intMeth.getValue()) {
304
305                                         List<String> methParams = intDecl.getMethodParams(method);
306                                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
307                                         print("public " + intDecl.getMethodType(method) + " " +
308                                                 intDecl.getMethodId(method) + "(");
309                                         for (int i = 0; i < methParams.size(); i++) {
310                                                 String paramType = checkAndGetParamClass(methPrmTypes.get(i), false);
311                                                 print(paramType + " " + methParams.get(i));
312                                                 //print(methPrmTypes.get(i) + " " + methParams.get(i));
313                                                 // Check if this is the last element (don't print a comma)
314                                                 if (i != methParams.size() - 1) {
315                                                         print(", ");
316                                                 }
317                                         }
318                                         println(");");
319                                 }
320                                 println("}");
321                                 pw.close();
322                                 System.out.println("IoTCompiler: Generated interface " + newIntface + ".java...");
323                         }
324                 }
325         }
326
327
328         /**
329          * generateCPlusInterfaces() generate stub interfaces based on the methods list in C++
330          * <p>
331          * For C++ we use virtual classe as interface
332          */
333         public void generateCPlusInterfaces() throws IOException {
334
335                 // Create a new directory
336                 String path = createDirectories(dir, subdir);
337                 for (String intface : mapIntfacePTH.keySet()) {
338
339                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
340                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
341
342                                 // Open a new file to write into
343                                 String newIntface = intMeth.getKey();
344                                 FileWriter fw = new FileWriter(path + "/" + newIntface + ".hpp");
345                                 pw = new PrintWriter(new BufferedWriter(fw));
346                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
347                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
348                                 // Write file headers
349                                 println("#include <iostream>");
350                                 // Pass in set of methods and get import classes
351                                 Set<String> includeClasses = getIncludeClasses(intMeth.getValue(), intDecl);
352                                 printIncludeStatements(includeClasses);
353                                 println("");
354                                 println("using namespace std;");
355                                 println("");
356                                 println("class " + newIntface);
357                                 println("{");
358                                 println("public:");
359                                 // Write methods
360                                 for (String method : intMeth.getValue()) {
361
362                                         List<String> methParams = intDecl.getMethodParams(method);
363                                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
364                                         print("virtual " + convertType(intDecl.getMethodType(method)) + " " +
365                                                 intDecl.getMethodId(method) + "(");
366                                         for (int i = 0; i < methParams.size(); i++) {
367                                                 String methPrmType = checkAndGetParamClass(methPrmTypes.get(i), true);
368                                                 methPrmType = checkAndGetCplusType(methPrmType);
369                                                 String methParamComplete = checkAndGetCplusArray(methPrmType, methParams.get(i));
370                                                 print(methParamComplete);
371                                                 // Check if this is the last element (don't print a comma)
372                                                 if (i != methParams.size() - 1) {
373                                                         print(", ");
374                                                 }
375                                         }
376                                         println(") = 0;");
377                                 }
378                                 print("}");
379                                 println(";");
380                                 pw.close();
381                                 System.out.println("IoTCompiler: Generated interface " + newIntface + ".hpp...");
382                         }
383                 }
384         }
385
386
387         /**
388          * generateJavaStubClasses() generate stubs based on the methods list in Java
389          */
390         public void generateJavaStubClasses() throws IOException {
391
392                 // Create a new directory
393                 String path = createDirectories(dir, subdir);
394                 for (String intface : mapIntfacePTH.keySet()) {
395
396                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
397                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
398
399                                 // Open a new file to write into
400                                 String newIntface = intMeth.getKey();
401                                 String newStubClass = newIntface + "_Stub";
402                                 FileWriter fw = new FileWriter(path + "/" + newStubClass + ".java");
403                                 pw = new PrintWriter(new BufferedWriter(fw));
404                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
405                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
406                                 // Pass in set of methods and get import classes
407                                 Set<String> importClasses = getImportClasses(intMeth.getValue(), intDecl);
408                                 printImportStatements(importClasses);
409                                 // Write interface header
410                                 println("");
411                                 println("public class " + newStubClass + " implements " + newIntface + " {");
412                                 println("");
413                                 // Write methods
414                                 for (String method : intMeth.getValue()) {
415
416                                         List<String> methParams = intDecl.getMethodParams(method);
417                                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
418                                         print("public " + intDecl.getMethodType(method) + " " +
419                                                 intDecl.getMethodId(method) + "(");
420                                         for (int i = 0; i < methParams.size(); i++) {
421
422                                                 print(methPrmTypes.get(i) + " " + methParams.get(i));
423                                                 // Check if this is the last element (don't print a comma)
424                                                 if (i != methParams.size() - 1) {
425                                                         print(", ");
426                                                 }
427                                         }
428                                         println(") {");
429                                         // Check if this is not "void"
430                                         if (!intDecl.getMethodType(method).equals("void")) {
431                                                 String retStmt = generateReturnStmt(intDecl.getMethodType(method));
432                                                 println("return " + retStmt + ";");
433                                         }
434                                         println("}"); println("");
435                                 }
436                                 println("}");
437                                 pw.close();
438                                 System.out.println("IoTCompiler: Generated stub class " + newStubClass + ".java...");
439                         }
440                 }
441         }
442
443
444         /**
445          * generateCPlusStubClasses() generate stubs based on the methods list in C++
446          */
447         public void generateCPlusStubClasses() throws IOException {
448
449                 // Create a new directory
450                 String path = createDirectories(dir, subdir);
451                 for (String intface : mapIntfacePTH.keySet()) {
452
453                         Map<String,Set<String>> mapNewIntMethods = mapInt2NewInts.get(intface);
454                         for (Map.Entry<String,Set<String>> intMeth : mapNewIntMethods.entrySet()) {
455                                 // Open a new file to write into
456                                 String newIntface = intMeth.getKey();
457                                 String newStubClass = newIntface + "_Stub";
458                                 FileWriter fw = new FileWriter(path + "/" + newStubClass + ".hpp");
459                                 pw = new PrintWriter(new BufferedWriter(fw));
460                                 // Write file headers
461                                 println("#include <iostream>");
462                                 println("#include \"" + newIntface + ".hpp\""); println("");            
463                                 println("using namespace std;"); println("");
464                                 println("class " + newStubClass + " : public " + newIntface); println("{");
465                                 println("public:"); println("");
466                                 // Add default constructor and destructor
467                                 println(newStubClass + "() { }"); println("");
468                                 println("~" + newStubClass + "() { }"); println("");
469                                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
470                                 InterfaceDecl intDecl = (InterfaceDecl) decHandler.getInterfaceDecl(intface);
471                                 // Write methods
472                                 for (String method : intMeth.getValue()) {
473
474                                         List<String> methParams = intDecl.getMethodParams(method);
475                                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
476                                         print(convertType(intDecl.getMethodType(method)) + " " +
477                                                 intDecl.getMethodId(method) + "(");
478                                         for (int i = 0; i < methParams.size(); i++) {
479                                                 String methPrmType = checkAndGetCplusType(methPrmTypes.get(i));
480                                                 String methParamComplete = checkAndGetCplusArray(methPrmType, methParams.get(i));
481                                                 print(methParamComplete);
482                                                 // Check if this is the last element (don't print a comma)
483                                                 if (i != methParams.size() - 1) {
484                                                         print(", ");
485                                                 }
486                                         }
487                                         println(") { ");
488                                         // Check if this is not "void"
489                                         if (!intDecl.getMethodType(method).equals("void")) {
490                                                 String retStmt = generateReturnStmt(intDecl.getMethodType(method));
491                                                 if (retStmt.equals("null")) { // null = NULL in C++
492                                                         retStmt = "NULL";
493                                                 }
494                                                 println("return " + retStmt + ";");
495                                         }
496                                         println("}"); println("");
497                                 }
498                                 print("}"); println(";");
499                                 pw.close();
500                                 System.out.println("IoTCompiler: Generated stub class " + newIntface + ".hpp...");
501                         }
502                 }
503         }
504
505
506         /**
507          * generateReturnStmt() generate return statement based on methType
508          */
509         public String generateReturnStmt(String methType) {
510
511                 // Generate dummy returns for now
512                 if (methType.equals("short")||
513                         methType.equals("int")  ||
514                         methType.equals("long") ||
515                         methType.equals("float")||
516                         methType.equals("double")) {
517
518                         return "1";
519                 } else if ( methType.equals("String")) {
520   
521                         return "\"a\"";
522                 } else if ( methType.equals("char") ||
523                                         methType.equals("byte")) {
524
525                         return "\'a\'";
526                 } else if ( methType.equals("boolean")) {
527
528                         return "true";
529                 } else {
530                         return "null";
531                 }
532         }
533
534
535         /**
536          * setDirectory() sets a new directory for stub files
537          */
538         public void setDirectory(String _subdir) {
539
540                 subdir = _subdir;
541         }
542
543
544         /**
545          * printUsage() prints the usage of this compiler
546          */
547         public static void printUsage() {
548
549                 System.out.println();
550                 System.out.println("Sentinel interface and stub compiler version 1.0");
551                 System.out.println("Copyright (c) 2015-2016 University of California, Irvine - Programming Language Group.");
552                 System.out.println("All rights reserved.");
553                 System.out.println("Usage:");
554                 System.out.println("\tjava IoTCompiler -help / --help / -h\n");
555                 System.out.println("\t\tDisplay this help texts\n\n");
556                 System.out.println("\tjava IoTCompiler [<main-policy-file> <req-policy-file>]");
557                 System.out.println("\tjava IoTCompiler [<main-policy-file> <req-policy-file>] [options]\n");
558                 System.out.println("\t\tTake one or more pairs of main-req policy files, and generate Java and/or C++ files\n");
559                 System.out.println("Options:");
560                 System.out.println("\t-java\t<directory>\tGenerate Java stub files");
561                 System.out.println("\t-cplus\t<directory>\tGenerate C++ stub files");
562                 System.out.println();
563         }
564
565
566         /**
567          * parseFile() prepares Lexer and Parser objects, then parses the file
568          */
569         public static ParseNode parseFile(String file) {
570
571                 ParseNode pn = null;
572                 try {
573                         ComplexSymbolFactory csf = new ComplexSymbolFactory();
574                         ScannerBuffer lexer = 
575                                 new ScannerBuffer(new Lexer(new BufferedReader(new FileReader(file)),csf));
576                         Parser parse = new Parser(lexer,csf);
577                         pn = (ParseNode) parse.parse().value;
578                 } catch (Exception e) {
579                         e.printStackTrace();
580                         throw new Error("IoTCompiler: ERROR parsing policy file or wrong command line option: " + file);
581                 }
582
583                 return pn;
584         }
585
586
587         /**================
588          * Helper functions
589          **================
590          */
591         boolean newline=true;
592         int tablevel=0;
593
594         private void print(String str) {
595                 if (newline) {
596                         int tab=tablevel;
597                         if (str.equals("}"))
598                                 tab--;
599                         for(int i=0; i<tab; i++)
600                                 pw.print("\t");
601                 }
602                 pw.print(str);
603                 updatetabbing(str);
604                 newline=false;
605         }
606
607         /**
608          * This function converts Java to C++ type for compilation
609          */
610         private String convertType(String jType) {
611
612                 return mapPrimitives.get(jType);
613         }
614
615
616         private void println(String str) {
617                 if (newline) {
618                         int tab = tablevel;
619                         if (str.equals("}"))
620                                 tab--;
621                         for(int i=0; i<tab; i++)
622                                 pw.print("\t");
623                 }
624                 pw.println(str);
625                 updatetabbing(str);
626                 newline = true;
627         }
628
629
630         private void updatetabbing(String str) {
631                 tablevel+=count(str,'{')-count(str,'}');
632         }
633
634
635         private int count(String str, char key) {
636                 char[] array = str.toCharArray();
637                 int count = 0;
638                 for(int i=0; i<array.length; i++) {
639                         if (array[i] == key)
640                                 count++;
641                 }
642                 return count;
643         }
644
645
646         private void createDirectory(String dirName) {
647
648                 File file = new File(dirName);
649                 if (!file.exists()) {
650                         if (file.mkdir()) {
651                                 System.out.println("IoTCompiler: Directory " + dirName + " has been created!");
652                         } else {
653                                 System.out.println("IoTCompiler: Failed to create directory " + dirName + "!");
654                         }
655                 } else {
656                         System.out.println("IoTCompiler: Directory " + dirName + " exists...");
657                 }
658         }
659
660
661         // Create a directory and possibly a sub directory
662         private String createDirectories(String dir, String subdir) {
663
664                 String path = dir;
665                 createDirectory(path);
666                 if (subdir != null) {
667                         path = path + "/" + subdir;
668                         createDirectory(path);
669                 }
670                 return path;
671         }
672
673
674         // Inserting array members into a Map object
675         // that maps arrKey to arrVal objects
676         private void arraysToMap(Map map, Object[] arrKey, Object[] arrVal) {
677
678                 for(int i = 0; i < arrKey.length; i++) {
679
680                         map.put(arrKey[i], arrVal[i]);
681                 }
682         }
683
684
685         // Return parameter category, i.e. PRIMITIVES, NONPRIMITIVES, or USERDEFINED
686         private ParamCategory getParamCategory(String paramType) {
687
688                 if (mapPrimitives.containsKey(paramType)) {
689                         return ParamCategory.PRIMITIVES;
690                 // We can either use mapNonPrimitivesJava or mapNonPrimitivesCplus here
691                 } else if (mapNonPrimitivesJava.containsKey(getSimpleType(paramType))) {
692                         return ParamCategory.NONPRIMITIVES;
693                 } else
694                         return ParamCategory.USERDEFINED;
695         }
696
697
698         // Return full class name for non-primitives to generate Java import statements
699         // e.g. java.util.Set for Set, java.util.Map for Map
700         private String getNonPrimitiveJavaClass(String paramNonPrimitives) {
701
702                 return mapNonPrimitivesJava.get(paramNonPrimitives);
703         }
704
705
706         // Return full class name for non-primitives to generate Cplus include statements
707         // e.g. #include <set> for Set, #include <map> for Map
708         private String getNonPrimitiveCplusClass(String paramNonPrimitives) {
709
710                 return mapNonPrimitivesCplus.get(paramNonPrimitives);
711         }
712
713
714         // Get simple types, e.g. HashSet for HashSet<...>
715         // Basically strip off the "<...>"
716         private String getSimpleType(String paramType) {
717
718                 // Check if this is generics
719                 if(paramType.contains("<")) {
720                         String[] type = paramType.split("<");
721                         return type[0];
722                 } else
723                         return paramType;
724         }
725
726
727         // Generate a set of classes for import statements
728         private Set<String> getImportClasses(Collection<String> methods, InterfaceDecl intDecl) {
729
730                 Set<String> importClasses = new HashSet<String>();
731                 for (String method : methods) {
732                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
733                         for (String paramType : methPrmTypes) {
734
735                                 String simpleType = getSimpleType(paramType);
736                                 if (getParamCategory(simpleType) == ParamCategory.NONPRIMITIVES) {
737                                         importClasses.add(getNonPrimitiveJavaClass(simpleType));
738                                 }
739                         }
740                 }
741                 return importClasses;
742         }
743
744
745         // Generate a set of classes for include statements
746         private Set<String> getIncludeClasses(Collection<String> methods, InterfaceDecl intDecl) {
747
748                 Set<String> includeClasses = new HashSet<String>();
749                 for (String method : methods) {
750
751                         List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
752                         List<String> methParams = intDecl.getMethodParams(method);
753                         for (int i = 0; i < methPrmTypes.size(); i++) {
754
755                                 String simpleType = getSimpleType(methPrmTypes.get(i));
756                                 String param = methParams.get(i);
757                                 if (getParamCategory(simpleType) == ParamCategory.NONPRIMITIVES) {
758                                         includeClasses.add("<" + getNonPrimitiveCplusClass(simpleType) + ">");
759                                 } else if (getParamCategory(simpleType) == ParamCategory.USERDEFINED) {
760                                         includeClasses.add("\"" + exchangeParamType(simpleType) + ".hpp\"");
761                                 } else if (param.contains("[]")) {
762                                 // Check if this is array for C++; translate into vector
763                                         includeClasses.add("<vector>");
764                                 }
765                         }
766                 }
767                 return includeClasses;
768         }
769
770
771         private void printImportStatements(Set<String> importClasses) {
772
773                 for(String cls : importClasses) {
774                         println("import " + cls + ";");
775                 }
776         }
777
778
779         private void printIncludeStatements(Set<String> includeClasses) {
780
781                 for(String cls : includeClasses) {
782                         println("#include " + cls);
783                 }
784         }
785
786
787         // Get the C++ version of a non-primitive type
788         // e.g. set for Set and map for Map
789         // Input nonPrimitiveType has to be generics in format
790         private String[] getTypeOfGeneric(String nonPrimitiveType) {
791
792                 // Handle <, >, and , for 2-type generic/template
793                 String[] substr = nonPrimitiveType.split("<")[1].split(">")[0].split(",");
794                 return substr;
795         }
796
797
798         private String checkAndGetCplusType(String paramType) {
799
800                 if (getParamCategory(paramType) == ParamCategory.PRIMITIVES) {
801                         return convertType(paramType);
802                 } else if (getParamCategory(paramType) == ParamCategory.NONPRIMITIVES) {
803
804                         // Check for generic/template format
805                         if (paramType.contains("<") && paramType.contains(">")) {
806
807                                 String genericClass = getSimpleType(paramType);
808                                 String[] genericType = getTypeOfGeneric(paramType);
809                                 String cplusTemplate = null;
810                                 if (genericType.length == 1) // Generic/template with one type
811                                         cplusTemplate = getNonPrimitiveCplusClass(genericClass) + 
812                                                 "<" + convertType(genericType[0]) + ">";
813                                 else // Generic/template with two types
814                                         cplusTemplate = getNonPrimitiveCplusClass(genericClass) + 
815                                                 "<" + convertType(genericType[0]) + "," + convertType(genericType[1]) + ">";
816                                 return cplusTemplate;
817                         } else
818                                 return getNonPrimitiveCplusClass(paramType);
819                 } else
820                         // Just return it as is if it's not non-primitives
821                         return paramType;
822         }
823
824
825         // Detect array declaration, e.g. int A[],
826         //              then generate "int A[]" in C++ as "vector<int> A"
827         private String checkAndGetCplusArray(String paramType, String param) {
828
829                 String paramComplete = null;
830                 // Check for array declaration
831                 if (param.contains("[]")) {
832                         paramComplete = "vector<" + paramType + "> " + param.replace("[]","");
833                 } else
834                         // Just return it as is if it's not an array
835                         paramComplete = paramType + " " + param;
836
837                 return paramComplete;
838         }
839
840
841         // Get simple types, e.g. HashSet for HashSet<...>
842         // Basically strip off the "<...>"
843         private String checkAndGetParamClass(String paramType, boolean needPtr) {
844
845                 // Check if this is generics
846                 if(getParamCategory(paramType) == ParamCategory.USERDEFINED) {
847                         // If true then return with pointer (C++)
848                         if (needPtr)
849                                 return exchangeParamType(paramType) + "*";
850                         else    // Java, so no pointer needed
851                                 return exchangeParamType(paramType);
852                 } else
853                         return paramType;
854         }
855
856
857         // Returns the other interface for type-checking purposes for USERDEFINED
858         //              classes based on the information provided in multiple policy files
859         // e.g. return CameraWithXXX instead of Camera
860         private String exchangeParamType(String intface) {
861
862                 // Param type that's passed is the interface name we need to look for
863                 //              in the map of interfaces, based on available policy files.
864                 DeclarationHandler decHandler = mapIntDeclHand.get(intface);
865                 if (decHandler != null) {
866                 // We've found the required interface policy files
867                         RequiresDecl reqDecl = (RequiresDecl) decHandler.getRequiresDecl(intface);
868                         Set<String> setExchInt = reqDecl.getInterfaces();
869                         if (setExchInt.size() == 1) {
870                                 Iterator iter = setExchInt.iterator();
871                                 return (String) iter.next();
872                         } else {
873                                 throw new Error("IoTCompiler: Ambiguous stub interfaces: " + setExchInt.toString() + 
874                                         ". Only one new interface can be declared if the object " + intface +
875                                         " needs to be passed in as an input parameter!");
876                         }
877                 } else {
878                 // NULL value - this means policy files missing
879                         throw new Error("IoTCompiler: Parameter type lookup failed for " + intface +
880                                 "... Please provide the necessary policy files for user-defined types." +
881                                 " If this is an array please type the brackets after the variable name," +
882                                 " e.g. \"String str[]\", not \"String[] str\"." +
883                                 " If this is a Collections (Java) / STL (C++) type, this compiler only" +
884                                 " supports List/ArrayList (Java) or list (C++).");
885                 }
886         }
887
888
889         public static void main(String[] args) throws Exception {
890
891                 // If there is no argument or just "--help" or "-h", then invoke printUsage()
892                 if ((args[0].equals("-help") ||
893                          args[0].equals("--help")||
894                          args[0].equals("-h"))   ||
895                         (args.length == 0)) {
896
897                         IoTCompiler.printUsage();
898
899                 } else if (args.length > 1) {
900
901                         IoTCompiler comp = new IoTCompiler();
902                         int i = 0;                              
903                         do {
904                                 // Parse main policy file
905                                 ParseNode pnPol = IoTCompiler.parseFile(args[i]);
906                                 // Parse "requires" policy file
907                                 ParseNode pnReq = IoTCompiler.parseFile(args[i+1]);
908                                 // Get interface name
909                                 String intface = ParseTreeHandler.getOrigIntface(pnPol);
910                                 comp.setParseTree(intface, pnPol, pnReq);
911                                 comp.getMethodsForIntface(intface);
912                                 i = i + 2;
913                         // 1) Check if this is the last option before "-java" or "-cplus"
914                         // 2) Check if this is really the last option (no "-java" or "-cplus")
915                         } while(!args[i].equals("-java") &&
916                                         !args[i].equals("-cplus") &&
917                                         (i < args.length));
918
919                         // Generate everything if we don't see "-java" or "-cplus"
920                         if (i == args.length) {
921                                 comp.generateJavaLocalInterfaces();
922                                 comp.generateJavaInterfaces();
923                                 comp.generateJavaStubClasses();
924                                 comp.generateCplusLocalInterfaces();
925                                 comp.generateCPlusInterfaces();
926                                 comp.generateCPlusStubClasses();
927                         } else {
928                         // Check other options
929                                 while(i < args.length) {
930                                         // Error checking
931                                         if (!args[i].equals("-java") &&
932                                                 !args[i].equals("-cplus")) {
933                                                 throw new Error("IoTCompiler: ERROR - unrecognized command line option: " + args[i]);
934                                         } else {
935                                                 if (i + 1 < args.length) {
936                                                         comp.setDirectory(args[i+1]);
937                                                 } else
938                                                         throw new Error("IoTCompiler: ERROR - please provide <directory> after option: " + args[i]);
939
940                                                 if (args[i].equals("-java")) {
941                                                         comp.generateJavaLocalInterfaces();
942                                                         comp.generateJavaInterfaces();
943                                                         comp.generateJavaStubClasses();
944                                                 } else {
945                                                         comp.generateCplusLocalInterfaces();
946                                                         comp.generateCPlusInterfaces();
947                                                         comp.generateCPlusStubClasses();
948                                                 }
949                                         }
950                                         i = i + 2;
951                                 }
952                         }
953                 } else {
954                 // Need to at least have exactly 2 parameters, i.e. main policy file and requires file
955                         IoTCompiler.printUsage();
956                         throw new Error("IoTCompiler: At least two arguments (main and requires policy files) have to be provided!");
957                 }
958         }
959 }
960
961
962
963