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