Initial implementation of JavaScript API check
authorIván Perdomo <ivan.perdomo@openbravo.com>
Tue, 28 Apr 2009 13:49:33 +0200
changeset 3838 e333a8b76c83
parent 3837 7390715b1638
child 3839 01c322bd3ff5
Initial implementation of JavaScript API check
.classpath.template
lib/build/js.jar
src-test/org/openbravo/test/javascript/JavaScriptAPIChecker.java
src-test/org/openbravo/test/javascript/JavaScriptAPITest.java
src-test/org/openbravo/test/javascript/JavaScriptParser.java
--- a/.classpath.template	Tue Apr 28 13:25:42 2009 +0200
+++ b/.classpath.template	Tue Apr 28 13:49:33 2009 +0200
@@ -14,5 +14,6 @@
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="lib" path="lib/runtime/org.openarchitectureware.core.workflow_4.3.1.20080910-1400PRD.jar"/>
 	<classpathentry kind="lib" path="lib/build/junit.jar"/>
+	<classpathentry kind="lib" path="lib/build/js.jar"/>
 	<classpathentry kind="output" path="build/classes"/>
 </classpath>
Binary file lib/build/js.jar has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src-test/org/openbravo/test/javascript/JavaScriptAPIChecker.java	Tue Apr 28 13:49:33 2009 +0200
@@ -0,0 +1,121 @@
+package org.openbravo.test.javascript;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.mozilla.javascript.FunctionNode;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.ScriptOrFnNode;
+import org.mozilla.javascript.Token;
+
+public class JavaScriptAPIChecker {
+  private HashMap<String, String> apiMap = new HashMap<String, String>();
+  private File apiDetailsFolder = null;
+  private File jsFolder = null;
+
+  public void setDetailsFolder(File details) {
+    if (!details.isDirectory()) {
+      throw new RuntimeException("A folder path must be passed as parameter");
+    }
+    apiDetailsFolder = details;
+  }
+
+  public void setJSFolder(File jsFolder) {
+    if (!jsFolder.isDirectory()) {
+      throw new RuntimeException("A folder path must be passed as parameter");
+    }
+    this.jsFolder = jsFolder;
+  }
+
+  public HashMap<String, String> getAPIMap() {
+    return apiMap;
+  }
+
+  public void process() {
+    if (apiDetailsFolder == null) {
+      throw new RuntimeException("A JavaScript API details folder must be set");
+    }
+
+    if (jsFolder == null) {
+      throw new RuntimeException("A folder containing the JavaScript files must be set");
+    }
+
+    FilenameFilter detailsFilter = new FilenameFilter() {
+      public boolean accept(File dir, String fileName) {
+        return fileName.endsWith(".details");
+      }
+    };
+
+    // Building a map with the current API details
+    final String[] detailFiles = apiDetailsFolder.list(detailsFilter);
+    for (int i = 0; i < detailFiles.length; i++) {
+      final File dFile = new File(apiDetailsFolder, detailFiles[i]);
+      int pos = detailFiles[i].indexOf(".details");
+      final String jsFileName = detailFiles[i].substring(0, pos);
+      String line;
+      int lineNo = 1;
+      try {
+        BufferedReader br = new BufferedReader(new FileReader(dFile));
+        while ((line = br.readLine()) != null) {
+          apiMap.put(jsFileName + line, String.valueOf(lineNo));
+          lineNo++;
+        }
+        br.close();
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+
+    // Parsing the .js files and checking it agains the api map
+
+    FilenameFilter jsFilter = new FilenameFilter() {
+      public boolean accept(File dir, String fileName) {
+        return fileName.endsWith(".js");
+      }
+    };
+
+    final JavaScriptParser jsp = new JavaScriptParser();
+
+    final String[] jsFiles = jsFolder.list(jsFilter);
+    for (int j = 0; j < jsFiles.length; j++) {
+      final File jsFile = new File(jsFolder, jsFiles[j]);
+      jsp.setFile(jsFile);
+      try {
+        checkJS(jsp, jsFiles[j]);
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  private void checkJS(JavaScriptParser p, String jsFileName) throws IOException {
+    ScriptOrFnNode nodeTree = p.parse();
+    for (Node cursor = nodeTree.getFirstChild(); cursor != null; cursor = cursor.getNext()) {
+      StringBuffer sb = new StringBuffer();
+      if (cursor.getType() == Token.FUNCTION) {
+        int fnIndex = cursor.getExistingIntProp(Node.FUNCTION_PROP);
+        FunctionNode fn = nodeTree.getFunctionNode(fnIndex);
+        Iterator<String> iter = null;
+        StringBuffer sbParam = new StringBuffer();
+        if (fn.getSymbolTable() != null) {
+          iter = fn.getSymbolTable().keySet().iterator();
+          while (iter.hasNext()) {
+            sbParam.append(iter.next());
+            sbParam.append(" ");
+          }
+        }
+        sb.append("FUNCTION: " + fn.getFunctionName() + " [ " + sbParam + "]");
+      } else if (cursor.getType() == Token.VAR) {
+        Node vn = cursor.getFirstChild();
+        sb.append("VAR: " + vn.getString());
+
+      }
+      apiMap.remove(jsFileName + sb);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src-test/org/openbravo/test/javascript/JavaScriptAPITest.java	Tue Apr 28 13:49:33 2009 +0200
@@ -0,0 +1,69 @@
+/**
+ * 
+ */
+package org.openbravo.test.javascript;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Properties;
+
+import junit.framework.TestCase;
+
+import org.openbravo.base.exception.OBException;
+import org.openbravo.base.session.OBPropertiesProvider;
+
+/**
+ * Generates a description file for each JavaScript in web/js
+ * 
+ * @author iperdomo
+ * 
+ */
+public class JavaScriptAPITest extends TestCase {
+
+  @Override
+  protected void setUp() throws Exception {
+    setConfigPropertyFiles();
+    super.setUp();
+  }
+
+  protected void setConfigPropertyFiles() {
+    // get the location of the current class file
+    final URL url = this.getClass().getResource(getClass().getSimpleName() + ".class");
+    File f = new File(url.getPath());
+    // go up 7 levels
+    for (int i = 0; i < 7; i++) {
+      f = f.getParentFile();
+    }
+    final File configDirectory = new File(f, "config");
+    f = new File(configDirectory, "Openbravo.properties");
+    if (!f.exists()) {
+      throw new OBException("The testrun assumes that it is run from "
+          + "within eclipse and that the Openbravo.properties "
+          + "file is located as a grandchild of the 7th ancestor " + "of this class");
+    }
+    OBPropertiesProvider.getInstance().setProperties(f.getAbsolutePath());
+  }
+
+  public void testJavaScriptAPI() {
+    JavaScriptAPIChecker apiCheck = new JavaScriptAPIChecker();
+    final Properties props = OBPropertiesProvider.getInstance().getOpenbravoProperties();
+    final String sourcePath = (String) props.get("source.path");
+    apiCheck.setDetailsFolder(new File(sourcePath + File.separator + "config"));
+    apiCheck.setJSFolder(new File(sourcePath + File.separator + "web" + File.separator + "js"));
+    apiCheck.process();
+    assertTrue("API Map should be empty: " + apiCheck.getAPIMap(), apiCheck.getAPIMap().isEmpty());
+  }
+
+  /*
+   * public void testJSDetailFiles() { FilenameFilter jsFilter = new FilenameFilter() { public
+   * boolean accept(File dir, String fileName) { return fileName.endsWith(".js"); } }; Properties
+   * props = OBPropertiesProvider.getInstance().getOpenbravoProperties(); final String sourcePath =
+   * (String) props.get("source.path"); final String tmpPath = System.getProperty("java.io.tmpdir");
+   * 
+   * File jsFolder = new File(sourcePath + File.separator + "web" + File.separator + "js"); String[]
+   * jsFiles = jsFolder.list(jsFilter); JavaScriptParser jsp = new JavaScriptParser(); for (int i =
+   * 0; i < jsFiles.length; i++) { File js = new File(jsFolder, jsFiles[i]); System.out.println(js);
+   * jsp.setFile(js); File jsDetils = new File(tmpPath, jsFiles[i] + ".details"); try {
+   * jsp.toFile(jsDetils); } catch (IOException e) { e.printStackTrace(); } } }
+   */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src-test/org/openbravo/test/javascript/JavaScriptParser.java	Tue Apr 28 13:49:33 2009 +0200
@@ -0,0 +1,121 @@
+package org.openbravo.test.javascript;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Iterator;
+
+import org.mozilla.javascript.CompilerEnvirons;
+import org.mozilla.javascript.ErrorReporter;
+import org.mozilla.javascript.FunctionNode;
+import org.mozilla.javascript.Node;
+import org.mozilla.javascript.Parser;
+import org.mozilla.javascript.ScriptOrFnNode;
+import org.mozilla.javascript.Token;
+
+public class JavaScriptParser {
+
+  private File jsFile = null;
+  private ScriptOrFnNode nodeTree = null;
+  private StringBuffer details = null;
+
+  public JavaScriptParser() {
+  }
+
+  /**
+   * Sets the file to parse
+   * 
+   * @param f
+   *          a File object of the JavaScript to parse
+   */
+  public void setFile(File f) {
+    jsFile = f;
+    nodeTree = null;
+    details = null;
+  }
+
+  /**
+   * Returns a tree representation of the parsed JavaScript file
+   * 
+   * @return
+   * @throws IOException
+   */
+  public ScriptOrFnNode parse() throws IOException {
+
+    if (nodeTree == null) {
+      Reader reader = new FileReader(jsFile);
+
+      CompilerEnvirons compilerEnv = new CompilerEnvirons();
+      ErrorReporter errorReporter = compilerEnv.getErrorReporter();
+
+      Parser parser = new Parser(compilerEnv, errorReporter);
+
+      String sourceURI;
+
+      try {
+        sourceURI = jsFile.getCanonicalPath();
+      } catch (IOException e) {
+        sourceURI = jsFile.toString();
+      }
+
+      nodeTree = parser.parse(reader, sourceURI, 1);
+    }
+    return nodeTree;
+  }
+
+  /**
+   * Returns a string with the global variables and function definitions
+   * 
+   * @return
+   */
+  public String getStringDetails() {
+    if (jsFile == null) {
+      throw new RuntimeException("You need to specify the file to parse");
+    }
+    if (details == null) {
+      details = new StringBuffer();
+      try {
+        parse();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+      for (Node cursor = nodeTree.getFirstChild(); cursor != null; cursor = cursor.getNext()) {
+        if (cursor.getType() == Token.FUNCTION) {
+          int fnIndex = cursor.getExistingIntProp(Node.FUNCTION_PROP);
+          FunctionNode fn = nodeTree.getFunctionNode(fnIndex);
+          Iterator<String> iter = null;
+          StringBuffer sbParam = new StringBuffer();
+          if (fn.getSymbolTable() != null) {
+            iter = fn.getSymbolTable().keySet().iterator();
+            while (iter.hasNext()) {
+              sbParam.append(iter.next());
+              sbParam.append(" ");
+            }
+          }
+          details.append("FUNCTION: " + fn.getFunctionName() + " [ " + sbParam + "]\n");
+        } else if (cursor.getType() == Token.VAR) {
+          Node vn = cursor.getFirstChild();
+          details.append("VAR: " + vn.getString() + "\n");
+        }
+      }
+    }
+    return details.toString();
+  }
+
+  /**
+   * Write the details of the js file into a file
+   * 
+   * @param f
+   */
+  public void toFile(File fileName) throws IOException {
+    if (jsFile == null) {
+      throw new RuntimeException("You need to specify the file to parse");
+    }
+    BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
+    out.write(getStringDetails());
+    out.close();
+  }
+}