+package iotpolicy;
+
+import java_cup.runtime.ComplexSymbolFactory;
+import java_cup.runtime.ScannerBuffer;
+import java.io.*;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import iotpolicy.parser.Lexer;
+import iotpolicy.parser.Parser;
+import iotpolicy.tree.ParseNode;
+import iotpolicy.tree.ParseNodeVector;
+import iotpolicy.tree.ParseTreeHandler;
+import iotpolicy.tree.CapabilityDecl;
+import iotpolicy.tree.InterfaceDecl;
+import iotpolicy.tree.RequiresDecl;
+
+/** Class IoTCompiler is the main stub compiler for
+ * stub files generation. This class calls helper classes
+ * such as Parser, Lexer, InterfaceDecl, CapabilityDecl,
+ * RequiresDecl, ParseTreeHandler, etc.
+ *
+ * @author Rahmadi Trimananda <rahmadi.trimananda @ uci.edu>
+ * @version 1.0
+ * @since 2016-09-22
+ */
+public class IoTCompiler {
+
+ /**
+ * Class properties
+ */
+ private String origInt;
+ private ParseTreeHandler ptHandler;
+ private InterfaceDecl intDecl;
+ private CapabilityDecl capDecl;
+ private RequiresDecl reqDecl;
+ private Map<String,Set<String>> mapCapabMethods;
+ private PrintWriter pw;
+ private String dir;
+
+ /**
+ * Class constants
+ */
+ private final static String OUTPUT_DIRECTORY = "stubfiles";
+
+ /**
+ * Class constructors
+ */
+ public IoTCompiler() {
+
+ origInt = null;
+ ptHandler = new ParseTreeHandler();
+ intDecl = null;
+ capDecl = null;
+ capDecl = null;
+ mapCapabMethods = new HashMap<String,Set<String>>();
+ pw = null;
+ dir = OUTPUT_DIRECTORY;
+ }
+
+
+ public IoTCompiler(String _origInt, ParseNode _pnPol, ParseNode _pnReq) {
+
+ origInt = _origInt;
+ ptHandler = new ParseTreeHandler(_origInt, _pnPol, _pnReq);
+ intDecl = null;
+ capDecl = null;
+ reqDecl = null;
+ mapCapabMethods = new HashMap<String,Set<String>>();
+ pw = null;
+ dir = OUTPUT_DIRECTORY;
+ }
+
+
+ /**
+ * parsePolicyFile() parses policy file
+ * <p>
+ * It also generates parse tree and
+ * copies useful information from parse tree into
+ * InterfaceDecl, CapabilityDecl, and RequiresDecl
+ * data structures.
+ * Additionally, the data structure handles are
+ * returned from tree-parsing for further process.
+ *
+ */
+ public void parsePolicyFile() {
+
+ ptHandler.processInterfaceDecl();
+ intDecl = ptHandler.getInterfaceDecl();
+
+ ptHandler.processCapabilityDecl();
+ capDecl = ptHandler.getCapabilityDecl();
+
+ ptHandler.processRequiresDecl();
+ reqDecl = ptHandler.getRequiresDecl();
+ }
+
+
+ /**
+ * getMethodsForIntface() reads for methods in the data structure
+ * <p>
+ * It is going to give list of methods for a certain interface
+ * based on the declaration of capabilities.
+ */
+ public void getMethodsForIntface() {
+
+ // Get set of new interfaces, e.g. CameraWithCaptureAndData
+ // Generate this new interface with all the methods it needs
+ // from different capabilities it declares
+ Set<String> setIntfaces = reqDecl.getInterfaces();
+ for (String strInt : setIntfaces) {
+
+ // Initialize a set of methods
+ Set<String> setMethods = new HashSet<String>();
+ // Get list of capabilities, e.g. ImageCapture, VideoRecording, etc.
+ List<String> listCapab = reqDecl.getCapabList(strInt);
+ for (String strCap : listCapab) {
+
+ // Get list of methods for each capability
+ List<String> listCapabMeth = capDecl.getMethods(strCap);
+ for (String strMeth : listCapabMeth) {
+
+ // Add methods into setMethods
+ // This is to also handle redundancies (say two capabilities
+ // share the same methods)
+ setMethods.add(strMeth);
+ }
+ }
+ // Add interface and methods information into map
+ mapCapabMethods.put(strInt, setMethods);
+ }
+ }
+
+
+ /**
+ * generateJavaInterfaces() generate stub interfaces based on the methods list in Java
+ */
+ public void generateJavaInterfaces() throws IOException {
+
+ // Create a new directory
+ createDirectory(dir);
+ for (Map.Entry<String,Set<String>> intMeth : mapCapabMethods.entrySet()) {
+
+ // Open a new file to write into
+ String newIntface = intMeth.getKey();
+ FileWriter fw = new FileWriter(dir + "/" + newIntface + ".java");
+ pw = new PrintWriter(new BufferedWriter(fw));
+ // Write interface header
+ println("");
+ println("public interface " + newIntface + " {");
+ List<String> meths = intDecl.getMethods();
+
+ // Write methods
+ for (String method : intMeth.getValue()) {
+
+ List<String> methParams = intDecl.getMethodParams(method);
+ List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
+ print("public " + intDecl.getMethodType(method) + " " +
+ intDecl.getMethodId(method) + "(");
+ for (int i = 0; i < methParams.size(); i++) {
+ print(methPrmTypes.get(i) + " " + methParams.get(i));
+ // Check if this is the last element (don't print a comma)
+ if (i != methParams.size() - 1) {
+ print(", ");
+ }
+ }
+ println(");");
+ }
+ println("}");
+ pw.close();
+ System.out.println("IoTCompiler: Generated interface " + newIntface + ".java...");
+ }
+ }
+
+
+ /**
+ * generateCPlusInterfaces() generate stub interfaces based on the methods list in C++
+ * <p>
+ * For C++ we use virtual classe as interface
+ */
+ public void generateCPlusInterfaces() throws IOException {
+
+ // Create a new directory
+ createDirectory(dir);
+ for (Map.Entry<String,Set<String>> intMeth : mapCapabMethods.entrySet()) {
+
+ // Open a new file to write into
+ String newIntface = intMeth.getKey();
+ FileWriter fw = new FileWriter(dir + "/" + newIntface + ".hpp");
+ pw = new PrintWriter(new BufferedWriter(fw));
+ // Write file headers
+ println("#include<iostream>");
+ println("");
+ println("using namespace std;");
+ println("");
+ println("class " + newIntface);
+ println("{");
+ println("public:");
+ // Write methods
+ for (String method : intMeth.getValue()) {
+
+ List<String> methParams = intDecl.getMethodParams(method);
+ List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
+ print("virtual " + convertType(intDecl.getMethodType(method)) + " " +
+ intDecl.getMethodId(method) + "(");
+ for (int i = 0; i < methParams.size(); i++) {
+ print(convertType(methPrmTypes.get(i)) + " " + methParams.get(i));
+ // Check if this is the last element (don't print a comma)
+ if (i != methParams.size() - 1) {
+ print(", ");
+ }
+ }
+ println(") = 0;");
+ }
+ print("}");
+ println(";");
+ pw.close();
+ System.out.println("IoTCompiler: Generated interface " + newIntface + ".hpp...");
+ }
+ }
+
+
+ /**
+ * generateJavaStubClasses() generate stubs based on the methods list in Java
+ */
+ public void generateJavaStubClasses() throws IOException {
+
+ // Create a new directory
+ createDirectory(dir);
+ for (Map.Entry<String,Set<String>> intMeth : mapCapabMethods.entrySet()) {
+
+ // Open a new file to write into
+ String newIntface = intMeth.getKey();
+ String newStubClass = newIntface + "_Stub";
+ FileWriter fw = new FileWriter(dir + "/" + newStubClass + ".java");
+ pw = new PrintWriter(new BufferedWriter(fw));
+ // Write interface header
+ println("");
+ println("public class " + newStubClass + " implements " + newIntface + " {");
+ println("");
+ // Write methods
+ for (String method : intMeth.getValue()) {
+
+ List<String> methParams = intDecl.getMethodParams(method);
+ List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
+ print("public " + intDecl.getMethodType(method) + " " +
+ intDecl.getMethodId(method) + "(");
+ for (int i = 0; i < methParams.size(); i++) {
+ print(methPrmTypes.get(i) + " " + methParams.get(i));
+ // Check if this is the last element (don't print a comma)
+ if (i != methParams.size() - 1) {
+ print(", ");
+ }
+ }
+ println(") {");
+ // Check if this is not "void"
+ if (!intDecl.getMethodType(method).equals("void")) {
+ String retStmt = generateReturnStmt(intDecl.getMethodType(method));
+ println("return " + retStmt + ";");
+ }
+ println("}");
+ println("");
+ }
+ println("}");
+ pw.close();
+ System.out.println("IoTCompiler: Generated stub class " + newStubClass + ".java...");
+ }
+ }
+
+
+ /**
+ * generateCPlusStubClasses() generate stubs based on the methods list in C++
+ */
+ public void generateCPlusStubClasses() throws IOException {
+
+ // Create a new directory
+ createDirectory(dir);
+ for (Map.Entry<String,Set<String>> intMeth : mapCapabMethods.entrySet()) {
+
+ // Open a new file to write into
+ String newIntface = intMeth.getKey();
+ String newStubClass = newIntface + "_Stub";
+ FileWriter fw = new FileWriter(dir + "/" + newStubClass + ".hpp");
+ pw = new PrintWriter(new BufferedWriter(fw));
+ // Write file headers
+ println("#include<iostream>");
+ println("#include \"" + newIntface + ".hpp\""); println("");
+ println("using namespace std;"); println("");
+ println("class " + newStubClass + " : public " + newIntface);
+ println("{");
+ println("public:"); println("");
+ // Add default constructor and destructor
+ println(newStubClass + "() { }"); println("");
+ println("~" + newStubClass + "() { }"); println("");
+ // Write methods
+ for (String method : intMeth.getValue()) {
+
+ List<String> methParams = intDecl.getMethodParams(method);
+ List<String> methPrmTypes = intDecl.getMethodParamTypes(method);
+ print(convertType(intDecl.getMethodType(method)) + " " +
+ intDecl.getMethodId(method) + "(");
+ for (int i = 0; i < methParams.size(); i++) {
+ print(convertType(methPrmTypes.get(i)) + " " + methParams.get(i));
+ // Check if this is the last element (don't print a comma)
+ if (i != methParams.size() - 1) {
+ print(", ");
+ }
+ }
+ println(") { ");
+ // Check if this is not "void"
+ if (!intDecl.getMethodType(method).equals("void")) {
+ String retStmt = generateReturnStmt(intDecl.getMethodType(method));
+ if (retStmt.equals("null")) { // null = NULL in C++
+ retStmt = "NULL";
+ }
+ println("return " + retStmt + ";");
+ }
+ println("}"); println("");
+ }
+ print("}"); println(";");
+ pw.close();
+ System.out.println("IoTCompiler: Generated stub class " + newIntface + ".hpp...");
+ }
+ }
+
+
+ /**
+ * generateReturnStmt() generate return statement based on methType
+ */
+ public String generateReturnStmt(String methType) {
+
+ // Generate dummy returns for now
+ if (methType.equals("short")||
+ methType.equals("int") ||
+ methType.equals("long") ||
+ methType.equals("float")||
+ methType.equals("double")) {
+
+ return "1";
+ } else if ( methType.equals("String") ||
+ methType.equals("byte")) {
+
+ return "\"a\"";
+ } else if ( methType.equals("char")) {
+
+ return "\'a\'";
+ } else if ( methType.equals("boolean")) {
+
+ return "true";
+ } else {
+ return "null";
+ }
+ }
+
+
+ /**
+ * setDirectory() set a new directory for stub files
+ */
+ public void setDirectory(String _dir) {
+
+ dir = _dir;
+ }
+
+
+ /**
+ * printUsage() prints the usage of this compiler
+ */
+ public static void printUsage() {
+
+ System.out.println();
+ System.out.println("Sentinel interface and stub compiler version 1.0");
+ System.out.println("Copyright (c) 2015-2016 University of California, Irvine - Programming Language Group.");
+ System.out.println("All rights reserved.");
+ System.out.println("Usage:");
+ System.out.println("\tjava IoTCompiler --help / -h\t\t\t\t\tDisplay this help texts");
+ System.out.println("\tjava IoTCompiler <main-policy-file> <req-policy-file>\t\tGenerate both Java and C++ stub files");
+ System.out.println("\tjava IoTCompiler <main-policy-file> <req-policy-file> [options]");
+ System.out.println("Options:");
+ System.out.println("\t-java\t<directory>\tGenerate Java stub files");
+ System.out.println("\t-cplus\t<directory>\tGenerate C++ stub files");
+ System.out.println();
+ }
+
+
+ /**================================================
+ * Helper functions to write stub codes into files
+ **================================================
+ */
+ boolean newline=true;
+ int tablevel=0;
+
+ private void print(String str) {
+ if (newline) {
+ int tab=tablevel;
+ if (str.equals("}"))
+ tab--;
+ for(int i=0; i<tab; i++)
+ pw.print("\t");
+ }
+ pw.print(str);
+ updatetabbing(str);
+ newline=false;
+ }
+
+ /**
+ * This function converts Java to C++ type for compilation
+ */
+ private String convertType(String jType) {
+
+ // Generate dummy returns for now
+ if (jType.equals("short")||
+ jType.equals("int") ||
+ jType.equals("long") ||
+ jType.equals("char") ||
+ jType.equals("float")||
+ jType.equals("double")) {
+
+ return jType;
+ } else if ( jType.equals("String")) {
+
+ return "string";
+ } else if ( jType.equals("byte")) {
+
+ return "char";
+ } else if ( jType.equals("boolean")) {
+
+ return "bool";
+ } else {
+ return jType;
+ }
+ }
+
+
+ private void println(String str) {
+ if (newline) {
+ int tab = tablevel;
+ if (str.equals("}"))
+ tab--;
+ for(int i=0; i<tab; i++)
+ pw.print("\t");
+ }
+ pw.println(str);
+ updatetabbing(str);
+ newline = true;
+ }
+
+
+ private void updatetabbing(String str) {
+ tablevel+=count(str,'{')-count(str,'}');
+ }
+
+
+ private int count(String str, char key) {
+ char[] array = str.toCharArray();
+ int count = 0;
+ for(int i=0; i<array.length; i++) {
+ if (array[i] == key)
+ count++;
+ }
+ return count;
+ }
+
+
+ private void createDirectory(String dirName) {
+
+ File file = new File(dirName);
+ if (!file.exists()) {
+ if (file.mkdir()) {
+ System.out.println("IoTCompiler: Directory " + dirName + " has been created!");
+ } else {
+ System.out.println("IoTCompiler: Failed to create directory " + dirName + "!");
+ }
+ } else {
+ System.out.println("IoTCompiler: Directory " + dirName + " exists...");
+ }
+ }
+
+
+ public static void main(String[] args) throws Exception {
+
+ // Runtime options
+ if (args.length != 0) {
+ // Display help
+ if ((args[0].equals("--help") ||
+ (args[0].equals("-h")))) {
+ IoTCompiler.printUsage();
+ } else {
+ // Parse main policy file
+ ComplexSymbolFactory csfPol = new ComplexSymbolFactory();
+ ScannerBuffer lexerPol =
+ new ScannerBuffer(new Lexer(new BufferedReader(new FileReader(args[0])),csfPol));
+ Parser parsePol = new Parser(lexerPol,csfPol);
+ ParseNode pnPol = (ParseNode) parsePol.parse().value;
+ // Parse "requires" policy file
+ ComplexSymbolFactory csfReq = new ComplexSymbolFactory();
+ ScannerBuffer lexerReq =
+ new ScannerBuffer(new Lexer(new BufferedReader(new FileReader(args[1])),csfReq));
+ Parser parseReq = new Parser(lexerReq,csfReq);
+ ParseNode pnReq = (ParseNode) parseReq.parse().value;
+ // Get interface name
+ String intFace = ParseTreeHandler.getOrigIntface(pnPol);
+ //System.out.println("IoTCompiler: Original interface: " + intFace);
+ IoTCompiler comp = new IoTCompiler(intFace, pnPol, pnReq);
+ // Generate all policy files if just policy file is provided
+ comp.parsePolicyFile();
+ comp.getMethodsForIntface();
+ if (args.length == 2) {
+ comp.generateJavaInterfaces();
+ comp.generateJavaStubClasses();
+ comp.generateCPlusInterfaces();
+ comp.generateCPlusStubClasses();
+ } else {
+ // Check other options
+ int i = 2;
+ while(i < args.length) {
+ // Check whether <directory> is provided
+ if ((i + 1) < args.length) {
+ comp.setDirectory(args[i+1]);
+ } else
+ throw new Error("IoTCompiler: ERROR - please provide <directory> after option: " + args[i]);
+ if (args[i].equals("-java")) {
+ comp.generateJavaInterfaces();
+ comp.generateJavaStubClasses();
+ } else if (args[i].equals("-cplus")) {
+ comp.generateCPlusInterfaces();
+ comp.generateCPlusStubClasses();
+ } else
+ throw new Error("IoTCompiler: ERROR - unrecognized command line option: " + args[i]);
+ i = i + 2;
+ }
+ }
+ }
+
+ } else {
+ // Need at least the policy file name
+ IoTCompiler.printUsage();
+ throw new Error("IoTCompiler: At least two arguments (main and requires policy files) have to be provided!");
+ }
+ }
+}
+
+
+
+