Updated dashboard to include method-level control-flow dependencies



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4374 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java b/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java
index 47c9c25..0107569 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java
@@ -53,6 +53,7 @@
   public static HashMap<String, HashSet<String>> storiesToCorrClassesAndMethods = new HashMap<String, HashSet<String>>();
   
   public static HashMap<String, String> storiesToLitType = new HashMap<String, String>();
+  
   public static void computePackageSizes(){
     cumSizeFromPackages = 0;
     packageToSize.clear();
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java b/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
index 7de2f8f..1d1a58c 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
@@ -23,11 +23,16 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
 
 public class MakeTopLevelHtmlForPerm {
   
@@ -416,20 +421,55 @@
     outFile.close();
   }
   
-  public static void copyFileOrDirectory(File srcPath, File dstPath) throws IOException{
+
+  public static void copyFileOrDirectoryFromJar(String jarFileName, String inputFileName, File dstPath, boolean isDirectory) throws IOException{
+
+    JarFile jarFile = new JarFile(jarFileName);
+    if (isDirectory){
+      dstPath.mkdir();
+      
+      JarInputStream jarFileIS = new JarInputStream(new FileInputStream(jarFileName));
+      JarEntry jarEntry = jarFileIS.getNextJarEntry();
+      while(jarEntry != null){
+        if (! inputFileName.endsWith("/")){
+          inputFileName += "/";
+        }
+        if ((jarEntry.getName().compareTo(inputFileName) != 0)&&(jarEntry.getName().startsWith(inputFileName))){
+          File newDstPath = new File(jarEntry.getName());          
+          copyFileOrDirectoryFromJar(jarFileName, jarEntry.getName(), newDstPath, false);
+        } 
+        jarEntry = jarFileIS.getNextJarEntry();
+      }
+      jarFileIS.close();
+    }
+
+    else{
+      InputStream in = jarFile.getInputStream(jarFile.getEntry(inputFileName));
+      OutputStream out = new FileOutputStream(dstPath); 
+      
+      int c;
+      while ((c = in.read()) != -1){
+        out.write(c);          
+      }
+      in.close();
+      out.close();
+      jarFile.close();
+    }
+  }
+  
+  public static void copyFileOrDirectory(File srcPath, File dstPath, String classPath, String inputFileName, boolean isDirectory) throws IOException{
     if (srcPath.isDirectory()){
       if (!dstPath.exists()){
         dstPath.mkdir();
      }
      String files[] = srcPath.list();
      for(int i = 0; i < files.length; i++){
-        copyFileOrDirectory(new File(srcPath, files[i]), new File(dstPath, files[i]));
+        copyFileOrDirectory(new File(srcPath, files[i]), new File(dstPath, files[i]), classPath, inputFileName, isDirectory);
       }
     }
    else{
       if(!srcPath.exists()){
-        System.out.println("File or directory does not exist.");
-       System.exit(0);
+        copyFileOrDirectoryFromJar(classPath, inputFileName, dstPath, isDirectory);
       }    
       else{
         InputStream in = new FileInputStream(srcPath);
@@ -631,11 +671,11 @@
       outFile.println("width:100%;");
       outFile.println("background-color:#eee;");
       outFile.println("border:1px solid  #666666;");
-      outFile.println("border-spacing:5px;/*cellspacing:poor IE support for  this*/");
+      outFile.println("border-spacing:5px;");
       outFile.println("border-collapse:separate;");
       outFile.println("}");
       outFile.println(".celldiv {");
-      outFile.println("float:left;/*fix for  buggy browsers*/");
+      outFile.println("float:left;");
       outFile.println("display:  table-cell;");
       outFile.println("width:49.5%;");
       outFile.println("font-size: 14px;");
@@ -679,22 +719,26 @@
   public static void makeHTMLShell(HashMap<String, CodeCollection> nameToCodeColl, TreeMap<String, LiteralsCollection> nameToLitColl) throws IOException{
     //this will contain the place holder iframes where the actual information is going to go.
     
+    System.out.println("making html shell");
     // copy from the bin directory to the current directory
     String classPath = System.getProperty("java.class.path");
     if (!classPath.endsWith("/")){
       classPath += "/";
     }
-    File inputFile = new File(classPath + "roundedCorners.css");
+    String inputFileName = "roundedCorners.css";
+    File inputFile = new File(classPath + inputFileName);
     File outputFile = new File("roundedCorners.css");
-    copyFileOrDirectory(inputFile, outputFile);
-  
-    File inputFile2 = new File(classPath + "classLevel.css");
+    copyFileOrDirectory(inputFile, outputFile, classPath, inputFileName, false);
+
+    inputFileName = "classLevel.css";
+    File inputFile2 = new File(classPath + inputFileName);
     File outputFile2 = new File("classLevel.css");
-    copyFileOrDirectory(inputFile2, outputFile2);
+    copyFileOrDirectory(inputFile2, outputFile2, classPath, inputFileName, false);
   
+    inputFileName = "images";
     File inputDir = new File(classPath + "images");
     File outputDir = new File("images");
-    copyFileOrDirectory(inputDir, outputDir);
+    copyFileOrDirectory(inputDir, outputDir, classPath, inputFileName, true);
     
     
     String fileName = "SoycDashboard-index.html";
@@ -723,6 +767,9 @@
     else{
       outFile.println("<b>Full code size: <span style=\"color:maroon\">" + GlobalInformation.cumSizeAllCode + "</span></b>");
     }
+    
+    outFile.println("<hr>");
+    
     outFile.println("</center>");
     outFile.println("  <div style=\"width:50%;  float:left; padding-top: 10px;\">");
     outFile.println("<b>Package breakdown</b>");
@@ -1086,8 +1133,8 @@
         String className = sortedClasses.get(size);
         
         //TODO(kprobst): switch out the commented/uncommented lines below when showing dependencies
-        float ratio = (size / maxSize) * 45;
-        //float ratio = (size / maxSize) * 85;
+        // float ratio = (size / maxSize) * 45;
+        float ratio = (size / maxSize) * 85;
          
         if (ratio < 3){
           ratio = 3;
@@ -1117,8 +1164,7 @@
         
         
         //TODO(kprobst): not currently used, but will be for dependencies
-        // place holder for mock-up of dependency display  
-        outFile.println("<div class=\"box-right\" style=\"width:" + depRatio + "%; top: " + yOffset + "px; left: 50%\">");
+/*        outFile.println("<div class=\"box-right\" style=\"width:" + depRatio + "%; top: " + yOffset + "px; left: 50%\">");
         outFile.println("<div id=\"lb\">");
         outFile.println("<div id=\"rb\">");
         outFile.println("<div id=\"bb\"><div id=\"blc\"><div id=\"brc\">");
@@ -1127,15 +1173,16 @@
         outFile.println("</div>");
         outFile.println("</div></div></div></div>");
         outFile.println("</div></div></div></div>");
-        outFile.println("</div>");
+        outFile.println("</div>");*/
               
         int yOffsetText = yOffset+8;
         outFile.printf("<div class=\"barlabel\" style=\"top:" + yOffsetText + "px; left:5px;\">%.1f</div>\n", size);
         outFile.println("<div class=\"barlabel\" style=\"top:" + yOffsetText + "px; left:70px;\">"+className+"</div>");
-        //TODO(kprobst) make this a link
+        
+/*        //TODO(kprobst) make this a link
         String drillDownFileName = className + "Deps.html";
         outFile.println("<div class=\"barlabel\" style=\"top:" + yOffsetText + "px; left:50%;\"><a href=\"" + drillDownFileName + "\" target=\"_top\">Dependencies: " + depCount + "</a></div>");
-        
+*/        
         yOffset = yOffset + 25;
   
       }
@@ -1204,11 +1251,11 @@
         outFile.println("width:100%;");
         outFile.println("background-color:#eee;");
         outFile.println("border:1px solid  #666666;");
-        outFile.println("border-spacing:5px;/*cellspacing:poor IE support for  this*/");
+        outFile.println("border-spacing:5px;");
         outFile.println("border-collapse:separate;");
         outFile.println("}");
         outFile.println(".celldiv {");
-        outFile.println("float:left;/*fix for  buggy browsers*/");
+        outFile.println("float:left;");
         outFile.println("display:  table-cell;");
         outFile.println("width:49.5%;");
         outFile.println("font-size: 14px;");
@@ -1238,7 +1285,21 @@
           }
         }
         for (String classOrMethod : sortedClassesAndMethods){
-          outFile.println("<div  class=\"rowdiv\">" + classOrMethod + "</div>");
+
+          //if it's a method
+          if ((classOrMethod.contains("("))&&(classOrMethod.contains(")"))){
+            //get the package
+            String packageName = classOrMethod;
+            packageName = packageName.replaceAll("\\.\\p{Upper}.*", "");
+            
+            String noParamMethod = classOrMethod;
+            noParamMethod = noParamMethod.replaceAll("\\(.*", "");
+            
+            outFile.println("<div  class=\"rowdiv\"><a href=\"methodDependencies-" + packageName + ".html#"+ noParamMethod + "\">" + classOrMethod + "</a></div>");
+          }
+          else{
+            outFile.println("<div  class=\"rowdiv\">" + classOrMethod + "</div>");            
+          }
         }
         
         outFile.println("</div>");
@@ -1298,7 +1359,21 @@
     outFile.println("<div  class=\"tablediv\">");  
     
     for (String classOrMethod : sortedClassesAndMethodsAllOtherFragments){
-      outFile.println("<div  class=\"rowdiv\">" + classOrMethod + "</div>");
+
+      //if it's a method
+      if ((classOrMethod.contains("("))&&(classOrMethod.contains(")"))){
+        //get the package
+        String packageName = classOrMethod;
+        packageName = packageName.replaceAll("\\.\\p{Upper}.*", "");
+        
+        String noParamMethod = classOrMethod;
+        noParamMethod = noParamMethod.replaceAll("\\(.*", "");
+        
+        outFile.println("<div  class=\"rowdiv\"><a href=\"methodDependencies-" + packageName + ".html#"+ noParamMethod + "\">" + classOrMethod + "</a></div>");
+      }
+      else{
+        outFile.println("<div  class=\"rowdiv\">" + classOrMethod + "</div>");
+      }
     }
     
     outFile.println("</div>");
@@ -1531,4 +1606,76 @@
       outFile.close();
     }
   }
+
+
+  
+  public static void makeDependenciesHtml(Map<String, ArrayList<String>> dependencies) throws IOException{
+
+    String origOutFileName = "methodDependencies-";
+    PrintWriter outFile = null;
+    String curPackageName = "";
+    
+    for (String method : dependencies.keySet()){
+      // this key set is already in alphabetical order
+      // get the package of this method, i.e., everything up to .[A-Z]
+      
+      String packageName = method;
+      packageName = packageName.replaceAll("\\.\\p{Upper}.*", "");
+      
+
+        if ((curPackageName.compareTo("") == 0) || (curPackageName.compareTo(packageName) != 0)){
+
+          curPackageName = packageName;
+          if (outFile != null){
+            //finish up the current file
+            outFile.println("</table>");
+            outFile.println("<center>");
+            
+            outFile.println("</div>");
+            outFile.println("</body>");
+            outFile.println("</html>");
+            outFile.close();
+          }
+          
+          String outFileName = origOutFileName + curPackageName + ".html";
+          outFile = new PrintWriter(outFileName); 
+          
+          outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
+          outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
+          outFile.println("<html>");
+          outFile.println("<head>");
+          outFile.println("<meta http-equiv=\"content-type\" content=\"text/html;charset=ISO-8859-1\">");
+          outFile.println("<title>Method Dependencies</title>");
+          outFile.println("</head>");
+      
+          outFile.println("<style type=\"text/css\">");
+          outFile.println("body {background-color: #728FCE}");
+          outFile.println("h2 {background-color: transparent}");
+          outFile.println("p {background-color: fuchsia}");
+          outFile.println("</style>");
+          
+          outFile.println("<body>");
+          outFile.println("<center>");
+          outFile.println("<h2>Method Dependencies</h2>");
+          outFile.println("</center>");
+          outFile.println("<hr>");
+          
+          outFile.println("<center>");      
+          outFile.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
+          
+        }
+        outFile.println("<tr>");
+        outFile.println("<td width=\"80%\"><a name=\"" + method + "\">" + method + "</a></td>");
+        outFile.println("</tr>");
+        
+        for (int i = 0; i < dependencies.get(method).size(); i++){
+          String depMethod = dependencies.get(method).get(i);
+          
+          outFile.println("<tr>");
+          outFile.println("<td width=\"20%\"></td>");
+          outFile.println("<td width=\"60%\">" + depMethod + "</td>");
+          outFile.println("</tr>");
+        }
+    }
+  }
 }
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java b/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java
index 4c8d760..5953fa4 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java
@@ -21,8 +21,10 @@
 import org.xml.sax.SAXException;
 
 import java.io.*;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.TreeSet;
 import java.util.TreeMap;
 import java.util.zip.GZIPInputStream;
@@ -37,13 +39,53 @@
    */
   public static void main(String[] args) {
     
-    if (args.length != 1){
-      System.err.println("Usage: java com/google/gwt/soyc/SoycDashboard soyc-report0.xml[.gz]");
+    if (args.length != 2){
+      System.err.println("Usage: java com/google/gwt/soyc/SoycDashboard soyc-report0.xml[.gz] soyc-dependencies0.xml[.gz]");
       System.exit(1);
     }
     
     String inFileName = args[0];
+    String depInFileName = args[1];
     
+    /**
+     * handle dependencies
+     */
+
+    Map<String, ArrayList<String>> dependencies = new TreeMap<String, ArrayList<String>>();
+    DefaultHandler depHandler = parseXMLDocumentDependencies(dependencies);
+    
+    // start parsing
+    SAXParserFactory depFactoryMain = SAXParserFactory.newInstance();
+    depFactoryMain.setNamespaceAware(true);
+    try {
+      SAXParser saxParser = depFactoryMain.newSAXParser();
+      InputStream in = new FileInputStream(depInFileName);
+      if (depInFileName.endsWith(".gz")) {
+        in = new GZIPInputStream(in);
+      }
+      in = new BufferedInputStream(in);
+      saxParser.parse(in,depHandler);
+    } catch (ParserConfigurationException e) {
+      throw new RuntimeException("Could not parse document. ", e);
+    } catch (SAXException e) {
+      throw new RuntimeException("Could not create SAX parser. ", e);
+    } catch (FileNotFoundException e) {
+      throw new RuntimeException("Could not open file. ", e);
+    } catch (IOException e) {
+      throw new RuntimeException("Could not open file. ", e);
+    }
+    
+    try{
+      MakeTopLevelHtmlForPerm.makeDependenciesHtml(dependencies);
+    } catch (IOException e) {
+      throw new RuntimeException("Cannot open file. ", e);
+    }
+
+    
+    /**
+     * handle everything else
+     */
+
     // to store literals data
     GlobalInformation.nameToLitColl.put("long",new LiteralsCollection("long"));
     GlobalInformation.nameToLitColl.put("null",new LiteralsCollection("null"));
@@ -69,7 +111,7 @@
     
     // make the parser handler
     DefaultHandler handler = parseXMLDocument(GlobalInformation.nameToLitColl, GlobalInformation.nameToCodeColl);
-    
+
     
     // start parsing
     SAXParserFactory factoryMain = SAXParserFactory.newInstance();
@@ -140,7 +182,7 @@
       public void startElement(String nsUri, String strippedName, String tagName, Attributes attributes) {
         
         if ((ct % 10000) == 0){
-          System.out.println(".");
+          System.out.println(ct);
         }
         ct++;
         
@@ -149,7 +191,7 @@
         if (strippedName.compareTo("splitpoint") == 0){
           System.out.println("about to process splitpoint");
          parseSplitPoint(attributes); 
-        }        
+        }
         else if ((strippedName.compareTo("package") == 0)&&(attributes.getValue("id") != null)){
           curPackage = attributes.getValue("id");
           
@@ -522,6 +564,13 @@
           if (attributes.getValue("literal") != null){
             curStoryLiteralType = attributes.getValue("literal");
             GlobalInformation.storiesToLitType.put(curStoryId, curStoryLiteralType);
+            
+
+            if (!nameToLitColl.get(curStoryLiteralType).storyToLocations.containsKey(curStoryId)){
+              HashSet<String> insertSet = new HashSet<String>();
+              nameToLitColl.get(curStoryLiteralType).storyToLocations.put(curStoryId, insertSet);
+            }
+            
           }
           else{
             curStoryLiteralType = "";
@@ -662,6 +711,55 @@
     return handler;
   }
   
+
+  
+  
+  
+
+
+  private static DefaultHandler parseXMLDocumentDependencies(final Map<String, ArrayList<String>> dependencies) { 
+    
+    DefaultHandler handler = new DefaultHandler() {
+
+      StringBuilder valueBuilder = new StringBuilder();
+      // may want to create a class for this later
+      String curMethod;
+        
+      /**
+       * This method deals with the beginning of the XML element.
+       * It analyzes the XML node and adds its information to the relevant literal or code collection for later analysis.
+       * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+       */
+      @Override
+      public void startElement(String nsUri, String strippedName, String tagName, Attributes attributes) {
+      
+        valueBuilder.delete(0,valueBuilder.length());
+        
+        
+        if ((strippedName.compareTo("method") == 0)&&(attributes.getValue("name") != null)){
+          curMethod = attributes.getValue("name");
+        }
+        else if ((strippedName.compareTo("called") == 0)&&(attributes.getValue("by") != null)){
+          String curDepMethod = attributes.getValue("by");
+          if (!dependencies.containsKey(curMethod)){
+            ArrayList<String> insertArray = new ArrayList<String>();
+            insertArray.add(curDepMethod);
+            dependencies.put(curMethod, insertArray);
+          }
+          else{
+            dependencies.get(curMethod).add(curDepMethod);
+          }
+        }
+      }
+
+    };
+    return handler;
+  }
+  
+
+  
+  
+  
   /*
    * cleans up the RPC code categories
    */
@@ -719,10 +817,6 @@
       //make the shell last so we can display aggregate information here
       MakeTopLevelHtmlForPerm.makeHTMLShell(nameToCodeColl, nameToLitColl);
       
-      
-
-      
-
     } catch (IOException e) {
       throw new RuntimeException("Cannot open file. ", e);
     }