Adjusts the SOYC dashboard to include a size breakdown of the
initial download.

Review by: knorton


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5298 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/tools/soyc-vis/build.xml b/tools/soyc-vis/build.xml
index c6d929d..6650e54 100644
--- a/tools/soyc-vis/build.xml
+++ b/tools/soyc-vis/build.xml
@@ -5,6 +5,9 @@
   <property name="gwt.root" location="../.." />
   <property name="project.tail" value="tools/soyc-vis" />
   <import file="${gwt.root}/common.ant.xml" />
+  
+  <!-- Platform shouldn't matter here, just picking one -->
+  <property.ensure name="gwt.dev.jar" location="${gwt.build.lib}/gwt-dev-linux.jar" />
 
   <target name="clean">
     <delete dir="build"/>
@@ -12,7 +15,11 @@
 
   <target name="compile">
     <mkdir dir="${javac.out}"/>
-    <javac srcdir="src" destdir="${javac.out}"/>
+    <javac srcdir="src" destdir="${javac.out}">
+      <classpath>
+        <pathelement location="${gwt.dev.jar}" />
+      </classpath>
+    </javac>
     <mkdir dir="${javac.out}/images"/>
     <copy todir="${javac.out}/images">
       <fileset dir="images"/>
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/CodeCollection.java b/tools/soyc-vis/src/com/google/gwt/soyc/CodeCollection.java
index 618e303..b05fead 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/CodeCollection.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/CodeCollection.java
@@ -34,29 +34,13 @@
     codeType = type;
   }
 
-  public float getCumPartialSize() {
+  public float getCumPartialSize(SizeBreakdown breakdown) {
     cumPartialSize = 0f;
     for (String className : classes) {
-      if (!GlobalInformation.classToPartialSize.containsKey(className)) {
-        System.err.println("*** NO PARTIAL SIZE FOUND FOR CLASS " + className
-            + " *****");
-      } else {
-        cumPartialSize += GlobalInformation.classToPartialSize.get(className);
+      if (breakdown.classToPartialSize.containsKey(className)) {
+        cumPartialSize += breakdown.classToPartialSize.get(className);
       }
     }
     return cumPartialSize;
   }
-
-  public int getCumSize() {
-    cumSize = 0;
-    for (String className : classes) {
-      if (!GlobalInformation.classToSize.containsKey(className)) {
-        System.err.println("*** NO SIZE FOUND FOR CLASS " + className
-            + " *****");
-      } else {
-        cumSize += GlobalInformation.classToSize.get(className);
-      }
-    }
-    return cumSize;
-  }
 }
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 e4c1f12..9e860e6 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/GlobalInformation.java
@@ -17,8 +17,9 @@
 package com.google.gwt.soyc;
 
 import java.util.HashMap;
-import java.util.TreeMap;
 import java.util.HashSet;
+import java.util.Map;
+import java.util.TreeMap;
 import java.util.TreeSet;
 
 /**
@@ -29,29 +30,16 @@
   public static Boolean displayDependencies = false;
   public static Boolean displaySplitPoints = false;
 
-  public static int allOtherFragmentsPartialSize = 0;
   public static HashMap<String, String> classToPackage = new HashMap<String, String>();
-  public static HashMap<String, Float> classToPartialSize = new HashMap<String, Float>();
-  public static HashMap<String, Integer> classToSize = new HashMap<String, Integer>();
   public static HashMap<String, HashSet<String>> classToWhatItDependsOn = new HashMap<String, HashSet<String>>();
-  public static float cumPartialSizeFromPackages = 0f;
-  public static int cumSizeAllCode = 0;
-  public static int cumSizeFromPackages = 0;
-  public static int cumSizeInitialFragment = 0;
-  // collect only size for exclusive fragments 
-  public static HashMap<Integer, Float> fragmentToPartialSize = new HashMap<Integer, Float>(); 
+  public static HashMap<Integer, Float> fragmentToPartialSize = new HashMap<Integer, Float>();
   public static HashMap<Integer, HashSet<String>> fragmentToStories = new HashMap<Integer, HashSet<String>>();
-  public static TreeMap<String, LiteralsCollection> nameToLitColl = new TreeMap<String, LiteralsCollection>();
-  public static HashMap<String, CodeCollection> nameToCodeColl = new HashMap<String, CodeCollection>();
 
-  public static int nonAttributedBytes = 0;
-  public static HashSet<String> nonAttributedStories = new HashSet<String>();
   public static int numBytesDoubleCounted = 0;
   public static int numFragments = 0;
   public static int numSplitPoints = 0;
-  public static TreeMap<String, TreeSet<String>> packageToClasses = new TreeMap<String, TreeSet<String>>();
-  public static HashMap<String, Float> packageToPartialSize = new HashMap<String, Float>();
-  public static HashMap<String, Integer> packageToSize = new HashMap<String, Integer>();
+  public static Map<String, TreeSet<String>> packageToClasses = new TreeMap<String, TreeSet<String>>();
+
   public static HashMap<Integer, String> splitPointToLocation = new HashMap<Integer, String>();
   public static HashMap<String, HashSet<String>> storiesToCorrClasses = new HashMap<String, HashSet<String>>();
   public static HashMap<String, HashSet<String>> storiesToCorrClassesAndMethods = new HashMap<String, HashSet<String>>();
@@ -60,18 +48,35 @@
 
   public static Settings settings = new Settings();
 
+  public static SizeBreakdown initialCodeBreakdown = new SizeBreakdown(
+      "Initially downloaded code", "initial");
+  public static SizeBreakdown totalCodeBreakdown = new SizeBreakdown(
+      "Total program", "total");
+
+  public static SizeBreakdown[] allSizeBreakdowns() {
+    return new SizeBreakdown[] {totalCodeBreakdown, initialCodeBreakdown};
+  }
+
   public static void computePackageSizes() {
-    cumSizeFromPackages = 0;
+    for (SizeBreakdown breakdown : allSizeBreakdowns()) {
+      computePackageSizes(breakdown.packageToSize, breakdown.classToSize);
+    }
+  }
+
+  public static void computePartialPackageSizes() {
+    for (SizeBreakdown breakdown : allSizeBreakdowns()) {
+      computePartialPackageSizes(breakdown.packageToPartialSize, breakdown.classToPartialSize);
+    }
+  }
+
+  private static void computePackageSizes(Map<String, Integer> packageToSize,
+      Map<String, Integer> classToSize) {
     packageToSize.clear();
     for (String packageName : packageToClasses.keySet()) {
       packageToSize.put(packageName, 0);
       for (String className : packageToClasses.get(packageName)) {
-        if (!classToSize.containsKey(className)) {
-          System.err.println("*** NO  SIZE FOUND FOR CLASS " + className
-              + " *****");
-        } else {
+        if (classToSize.containsKey(className)) {
           int curSize = classToSize.get(className);
-          cumSizeFromPackages += curSize;
           int newSize = curSize + packageToSize.get(packageName);
           packageToSize.put(packageName, newSize);
         }
@@ -79,16 +84,16 @@
     }
   }
 
-  public static void computePartialPackageSizes() {
-    cumPartialSizeFromPackages = 0;
+  private static void computePartialPackageSizes(
+      Map<String, Float> packageToPartialSize,
+      Map<String, Float> classToPartialSize) {
+    float cumPartialSizeFromPackages = 0f;
+
     packageToPartialSize.clear();
     for (String packageName : packageToClasses.keySet()) {
       packageToPartialSize.put(packageName, 0f);
       for (String className : packageToClasses.get(packageName)) {
-        if (!classToPartialSize.containsKey(className)) {
-          System.err.println("*** NO PARTIAL SIZE FOUND FOR CLASS " + className
-              + " *****");
-        } else {
+        if (classToPartialSize.containsKey(className)) {
           float curSize = classToPartialSize.get(className);
           cumPartialSizeFromPackages += curSize;
           float newSize = curSize + packageToPartialSize.get(packageName);
@@ -97,5 +102,4 @@
       }
     }
   }
-
 }
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 48e0473..c4d747a 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
@@ -16,13 +16,17 @@
 
 package com.google.gwt.soyc;
 
+import com.google.gwt.dev.util.Util;
+
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -37,7 +41,18 @@
  * A utility to make the top level HTTML file for one permutation.
  */
 public class MakeTopLevelHtmlForPerm {
+  /**
+   * By a convention shared with the compiler, the initial download is fragment
+   * number 0.
+   */
+  private static final int FRAGMENT_NUMBER_INITIAL_DOWNLOAD = 0;
 
+  /**
+   * Just within this file, the convention is that the total program is fragment
+   * number -1.
+   */
+  private static final int FRAGMENT_NUMBER_TOTAL_PROGRAM = -1;
+  
   public static void copyFileOrDirectory(File srcPath, File dstPath,
       String classPath, String inputFileName, boolean isDirectory)
       throws IOException {
@@ -116,26 +131,25 @@
     return escaped;
   }
 
-  public static void makeCodeTypeClassesHtmls(
-      HashMap<String, CodeCollection> nameToCodeColl) throws IOException {
+  public static void makeCodeTypeClassesHtmls(SizeBreakdown breakdown)
+      throws IOException {
+    HashMap<String, CodeCollection> nameToCodeColl = breakdown.nameToCodeColl;
 
     for (String codeType : nameToCodeColl.keySet()) {
 
       // construct file name
-      String outFileName = codeType + "Classes.html";
+      String outFileName = breakdown.getId() + "_" + codeType + "Classes.html";
 
       float maxSize = 0f;
       TreeMap<Float, String> sortedClasses = new TreeMap<Float, String>(
           Collections.reverseOrder());
+
       for (String className : nameToCodeColl.get(codeType).classes) {
-        if (GlobalInformation.classToPartialSize.containsKey(className)) {
+        if (breakdown.classToPartialSize.containsKey(className)) {
 
           float curSize = 0f;
-          if (!GlobalInformation.classToPartialSize.containsKey(className)) {
-            System.err.println("*** NO PARTIAL SIZE FOUND FOR CLASS "
-                + className + " *****");
-          } else {
-            curSize = GlobalInformation.classToPartialSize.get(className);
+          if (breakdown.classToPartialSize.containsKey(className)) {
+            curSize = breakdown.classToPartialSize.get(className);
           }
 
           if (curSize != 0f) {
@@ -168,10 +182,10 @@
 
       outFile.println("<center>");
       outFile.println("<h2>Classes of type \"" + codeType + "\"</h2>");
+      addHeaderWithBreakdownContext(breakdown, outFile);
       outFile.println("</center>");
-      outFile.println("<hr>");
 
-      outFile.println("<div style=\"width:90%; height:80%; overflow-y:auto; overflow-x:auto; top: 90px; left:70px; position:absolute; background-color:white\"");
+      outFile.println("<div style=\"width:90%; height:80%; overflow-y:auto; overflow-x:auto; top: 120px; left:70px; position:absolute; background-color:white\"");
 
       int yOffset = 0;
       for (Float size : sortedClasses.keySet()) {
@@ -212,7 +226,7 @@
     }
   }
 
-  public static void makeDependenciesHtml(
+  public void makeDependenciesHtml(
       Map<String, ArrayList<String>> dependencies) throws IOException {
 
     String origOutFileName = "methodDependencies-";
@@ -245,7 +259,8 @@
           outFile.close();
         }
 
-        String outFileName = origOutFileName + curPackageName + ".html";
+        String outFileName = origOutFileName + filename(curPackageName)
+            + ".html";
         outFile = new PrintWriter(outFileName);
 
         outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
@@ -300,12 +315,13 @@
     }
   }
 
-  public static void makeHTMLShell(
-      HashMap<String, CodeCollection> nameToCodeColl,
-      TreeMap<String, LiteralsCollection> nameToLitColl) throws IOException {
+  public static void makeBreakdownShell(SizeBreakdown breakdown) throws IOException {
     // this will contain the place holder iframes where the actual information
     // is going to go.
 
+    Map<String, CodeCollection> nameToCodeColl = breakdown.nameToCodeColl;
+    Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl;
+
     // copy from the bin directory to the current directory
     String classPath = GlobalInformation.settings.resources.get();
     if (classPath == null) {
@@ -330,15 +346,13 @@
     File outputDir = new File("images");
     copyFileOrDirectory(inputDir, outputDir, classPath, inputFileName, true);
 
-    String fileName = "SoycDashboard-index.html";
-
-    final PrintWriter outFile = new PrintWriter(fileName);
+    final PrintWriter outFile = new PrintWriter(shellFileName(breakdown));
     outFile.println("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">");
     outFile.println("<html>");
     outFile.println("<head>");
     outFile.println("<title>Story of Your Compile - Top Level Dashboard for Permutation</title>");
 
-    outFile.println("<link rel=\"stylesheet\" href=\"roundedcorners.css\">");
+    outFile.println("<link rel=\"stylesheet\" href=\"roundedCorners.css\">");
     outFile.println("<style type=\"text/css\">");
     outFile.println("body {background-color: #728FCE}");
     outFile.println("h2 {background-color: transparent}");
@@ -349,18 +363,7 @@
     outFile.println("<body>");
     outFile.println("<center>");
     outFile.println("<h3>Story of Your Compile Dashboard</h3>");
-    outFile.println("<hr>");
-    if (GlobalInformation.fragmentToStories.size() > 1) {
-      outFile.println("<b>Full code size: <span style=\"color:maroon\">"
-          + GlobalInformation.cumSizeAllCode
-          + "</span>, Size of Initial Download:  <span style=\"color:maroon\">"
-          + GlobalInformation.fragmentToPartialSize.get(0) + "</span></b>");
-    } else {
-      outFile.println("<b>Full code size: <span style=\"color:maroon\">"
-          + GlobalInformation.cumSizeAllCode + "</span></b>");
-    }
-
-    outFile.println("<hr>");
+    addHeaderWithBreakdownContext(breakdown, outFile);
 
     outFile.println("</center>");
     outFile.println("  <div style=\"width:50%;  float:left; padding-top: 10px;\">");
@@ -380,15 +383,18 @@
     outFile.println("<div style=\"width: 200px; float: left; text-align:left; font-size:16px; \">Code Type</div>");
     outFile.println("    </div>");
 
+    String packageBreakdownFileName = makePackageHtml(breakdown);
     outFile.println("<div style=\"height:35%; width:48%; margin:0 auto; background-color:white; float:left;\">");
-    outFile.println("<iframe src=\"packageBreakdown.html\" width=100% height=100% scrolling=auto></iframe>");
+    outFile.println("<iframe src=\"" + packageBreakdownFileName
+        + "\" width=100% height=100% scrolling=auto></iframe>");
     outFile.println("  </div>");
-    makePackageHtml("packageBreakdown.html");
 
+    String codeTypeBreakdownFileName = makeCodeTypeHtml(breakdown,
+        nameToCodeColl);
     outFile.println("<div style=\"height:35%; width:48%; margin:0 auto; background-color:white; float:right;\">");
-    outFile.println("<iframe src=\"codeTypeBreakdown.html\" width=100% height=100% scrolling=auto></iframe>");
+    outFile.println("<iframe src=\"" + codeTypeBreakdownFileName
+        + "\" width=100% height=100% scrolling=auto></iframe>");
     outFile.println("  </div>");
-    makeCodeTypeHtml("codeTypeBreakdown.html", nameToCodeColl);
 
     outFile.println("  <div style=\"width:50%;  float:left; padding-top: 10px;\">");
     outFile.println("<b>Literals breakdown</b>");
@@ -407,141 +413,34 @@
     outFile.println("<div style=\"width: 200px; float: left; text-align:left; font-size:16px; \">String Literal Type</div>");
     outFile.println("    </div>");
 
+    String literalsBreakdownFileName = makeLiteralsHtml(breakdown,
+        nameToLitColl);
     outFile.println("<div style=\"height:35%; width:48%; margin:0 auto; background-color:white; float:left;\">");
-    outFile.println("<iframe src=\"literalsBreakdown.html\" width=100% height=100% scrolling=auto></iframe>");
+    outFile.println("<iframe src=\"" + literalsBreakdownFileName
+        + "\" width=100% height=100% scrolling=auto></iframe>");
     outFile.println("</div>");
-    makeLiteralsHtml("literalsBreakdown.html", nameToLitColl);
 
+    String stringLiteralsBreakdownFileName = makeStringLiteralsHtml(breakdown,
+        nameToLitColl);
     outFile.println("<div style=\"height:35%; width:48%; margin:0 auto; background-color:white; float:right;\">");
-    outFile.println("<iframe src=\"stringLiteralsBreakdown.html\" width=100% height=100% scrolling=auto></iframe>");
+    outFile.println("<iframe src=\"" + stringLiteralsBreakdownFileName
+        + "\" width=100% height=100% scrolling=auto></iframe>");
     outFile.println("  </div>");
-    makeStringLiteralsHtml("stringLiteralsBreakdown.html", nameToLitColl);
-
-    if (GlobalInformation.fragmentToStories.size() > 1) {
-      outFile.println("  <div style=\"width:50%;  float:left; padding-top: 10px;\">");
-      outFile.println("<b>Breakdown of key runAsync fragments</b>");
-      outFile.println("    </div>");
-
-      outFile.println("  <div style=\"width:100%;  float:left; padding-top: 10px;\">");
-      outFile.println("<div style=\"width: 110px; float: left; font-size:16px;\">Size</div>");
-      outFile.println("<div style=\"width: 200px; float: left; text-align:left; font-size:16px; \">Fragment</div>");
-      outFile.println("    </div>");
-
-      outFile.println("<br style=\"clear:both\">");
-      makeFragmentsHtml(outFile);
-    }
 
     outFile.println("  </body>");
     outFile.println("</html>");
     outFile.close();
   }
 
-  public static void makeLiteralsClassesHtmls(
-      TreeMap<String, LiteralsCollection> nameToLitColl) throws IOException {
+  public static void makeLiteralsClassesTableHtmls(SizeBreakdown breakdown)
+      throws IOException {
+    Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl;
 
     for (String literalType : nameToLitColl.keySet()) {
 
       String outFileName = literalType + "Lits.html";
-      final PrintWriter 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>Literals of type \"" + literalType + "\"</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(".tablediv {");
-      outFile.println("display:  table;");
-      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-collapse:separate;");
-      outFile.println("}");
-      outFile.println(".celldiv {");
-      outFile.println("float:left;/*fix for  buggy browsers*/");
-      outFile.println("display:  table-cell;");
-      outFile.println("width:50%;");
-      outFile.println("font-size: 14px;");
-      outFile.println("background-color:white;");
-      outFile.println("}");
-      outFile.println(".rowdiv  {");
-      outFile.println("display:  table-row;");
-      outFile.println("}");
-      outFile.println("</style>");
-
-      outFile.println("<body>");
-      outFile.println("<center>");
-      outFile.println("<h2>Literals of type \"" + literalType + "\"</h2>");
-      outFile.println("</center>");
-      outFile.println("<hr>");
-
-      outFile.println("<div style=\"width:90%; height:80%; overflow-y:auto; overflow-x:auto; top: 30px; left:60px; position:relative; background-color:white\"");
-      outFile.println("<div class=\"tablediv\">");
-      for (String literal : nameToLitColl.get(literalType).literalToLocations.keySet()) {
-
-        if (literal.trim().compareTo("") == 0) {
-          literal = "[whitespace only string]";
-        }
-
-        String newLiteral = "";
-        if (literal.length() > 100) {
-          int i;
-          for (i = 100; i < literal.length(); i = i + 100) {
-            String part1 = literal.substring(i - 100, i);
-            newLiteral = newLiteral + part1 + " ";
-          }
-          if (i - 100 > 0) {
-            newLiteral = newLiteral + literal.substring(i - 100);
-          }
-        } else {
-          newLiteral = literal;
-        }
-
-        String escliteral = escapeXml(newLiteral);
-        outFile.println("<div class=\"rowdiv\">");
-        outFile.println("<div class=\"celldiv\">" + escliteral + "</div>");
-
-        for (String location : nameToLitColl.get(literalType).literalToLocations.get(literal)) {
-
-          String newLocation = "";
-          if (location.length() > 100) {
-            int i;
-            for (i = 100; i < location.length(); i = i + 100) {
-              String part1 = location.substring(i - 100, i);
-              newLocation = newLocation + part1 + " ";
-            }
-            if (i - 100 > 0) {
-              newLocation = newLocation + location.substring(i - 100);
-            }
-          } else {
-            newLocation = location;
-          }
-
-          outFile.println("<div class=\"celldiv\">" + newLocation + "</div>");
-        }
-        outFile.println("</div>");
-      }
-      outFile.println("</div>");
-      outFile.println("</body>");
-      outFile.println("</html>");
-      outFile.close();
-    }
-  }
-
-  public static void makeLiteralsClassesTableHtmls(
-      TreeMap<String, LiteralsCollection> nameToLitColl) throws IOException {
-
-    for (String literalType : nameToLitColl.keySet()) {
-
-      String outFileName = literalType + "Lits.html";
-      final PrintWriter outFile = new PrintWriter(outFileName);
+      final PrintWriter outFile = new PrintWriter(breakdown.getId() + "_"
+          + outFileName);
 
       outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
       outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
@@ -560,8 +459,8 @@
       outFile.println("<body>");
       outFile.println("<center>");
       outFile.println("<h2>Literals of type \"" + literalType + "\"</h2>");
+      addHeaderWithBreakdownContext(breakdown, outFile);
       outFile.println("</center>");
-      outFile.println("<hr>");
 
       outFile.println("<center>");
       outFile.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
@@ -640,11 +539,13 @@
     }
   }
 
-  public static void makePackageClassesHtmls() throws IOException {
-
+  /**
+   * Make size breakdowns for each package for one code collection.
+   */
+  public void makePackageClassesHtmls(SizeBreakdown breakdown)
+      throws IOException {
     for (String packageName : GlobalInformation.packageToClasses.keySet()) {
 
-      String outFileName = packageName + "Classes.html";
       TreeMap<Float, String> sortedClasses = new TreeMap<Float, String>(
           Collections.reverseOrder());
       float maxSize = 0f;
@@ -654,11 +555,10 @@
       for (String className : GlobalInformation.packageToClasses.get(packageName)) {
 
         float curSize = 0f;
-        if (!GlobalInformation.classToPartialSize.containsKey(className)) {
-          System.err.println("*** NO PARTIAL SIZE FOUND FOR CLASS " + className
-              + " *****");
+        if (!breakdown.classToPartialSize.containsKey(className)) {
+          // This class not present in this code collection
         } else {
-          curSize = GlobalInformation.classToPartialSize.get(className);
+          curSize = breakdown.classToPartialSize.get(className);
         }
 
         int depCount = 0;
@@ -678,7 +578,7 @@
         }
       }
 
-      final PrintWriter outFile = new PrintWriter(outFileName);
+      PrintWriter outFile = new PrintWriter(classesInPackageFileName(breakdown, packageName));
 
       outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
       outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
@@ -693,10 +593,10 @@
 
       outFile.println("<center>");
       outFile.println("<h2>Classes in package \"" + packageName + "\"</h2>");
+      addHeaderWithBreakdownContext(breakdown, outFile);
       outFile.println("</center>");
-      outFile.println("<hr>");
 
-      outFile.println("<div style=\"width:90%; height:80%; overflow-y:auto; overflow-x:auto; top: 90px; left:70px; position:absolute; background-color:white\"");
+      outFile.println("<div style=\"width:90%; height:80%; overflow-y:auto; overflow-x:auto; top: 120px; left:70px; position:absolute; background-color:white\"");
 
       int yOffset = 0;
       for (Float size : sortedClasses.keySet()) {
@@ -754,7 +654,7 @@
             + "px; left:5px;\">%.1f</div>\n", size);
         if (GlobalInformation.displayDependencies == true) {
           outFile.println("<div class=\"barlabel\" style=\"top:" + yOffsetText
-              + "px; left:70px;\"><a href=\"methodDependencies-" + packageName
+              + "px; left:70px;\"><a href=\"methodDependencies-" + filename(packageName)
               + ".html#" + className + "\">" + className + "</a></div>");
         } else {
           outFile.println("<div class=\"barlabel\" style=\"top:" + yOffsetText
@@ -780,9 +680,10 @@
    * Makes html file for fragment classes. TODO(kprobst): update this once we
    * have SOYC updated to supply enough information
    * 
-   * @throws IOException
+   * TODO(spoon) instead of this listing, make a size breakdown for each
+   * exclusive fragment
    */
-  public static void makeSplitPointClassesHtmls() throws IOException {
+  public void makeSplitPointClassesHtmls() throws IOException {
 
     // for the initial fragment and the fragments in the load order, we can
     // print this immediately
@@ -791,13 +692,18 @@
     TreeSet<String> sortedClassesAndMethodsAllOtherFragments = new TreeSet<String>();
 
     for (Integer fragmentName : GlobalInformation.fragmentToStories.keySet()) {
+      if (fragmentName == FRAGMENT_NUMBER_INITIAL_DOWNLOAD) {
+        /*
+         * For the initial download, a size breakdown is availale to replace the
+         * split point class listing
+         */
+        continue;
+      }
 
-      if ((fragmentName != 0)
-          && (!GlobalInformation.splitPointToLocation.containsKey(fragmentName))) {
-
+      if (!GlobalInformation.splitPointToLocation.containsKey(fragmentName)) {
         // get the stories from ALL the fragments
         for (String storyName : GlobalInformation.fragmentToStories.get(fragmentName)) {
-          if ((!GlobalInformation.nameToLitColl.get("string").storyToLocations.containsKey(storyName))
+          if ((!GlobalInformation.totalCodeBreakdown.nameToLitColl.get("string").storyToLocations.containsKey(storyName))
               && (GlobalInformation.storiesToCorrClassesAndMethods.containsKey(storyName))) {
             for (String className : GlobalInformation.storiesToCorrClassesAndMethods.get(storyName)) {
               sortedClassesAndMethodsAllOtherFragments.add(className);
@@ -807,14 +713,14 @@
       } else {
         String curSplitPointLocation;
 
-        if (fragmentName == 0) {
+        if (fragmentName == FRAGMENT_NUMBER_INITIAL_DOWNLOAD) {
           curSplitPointLocation = "initialDownload";
         } else {
           curSplitPointLocation = GlobalInformation.splitPointToLocation.get(fragmentName);
         }
 
-        String outFileName = "splitPoint" + curSplitPointLocation
-            + "Classes.html";
+        String outFileName = "splitPoint-" + filename(curSplitPointLocation)
+            + "-Classes.html";
 
         final PrintWriter outFile = new PrintWriter(outFileName);
 
@@ -864,7 +770,7 @@
 
         TreeSet<String> sortedClassesAndMethods = new TreeSet<String>();
         for (String storyName : GlobalInformation.fragmentToStories.get(fragmentName)) {
-          if ((!GlobalInformation.nameToLitColl.get("string").storyToLocations.containsKey(storyName))
+          if ((!GlobalInformation.totalCodeBreakdown.nameToLitColl.get("string").storyToLocations.containsKey(storyName))
               && (GlobalInformation.storiesToCorrClassesAndMethods.containsKey(storyName))) {
             for (String className : GlobalInformation.storiesToCorrClassesAndMethods.get(storyName)) {
               sortedClassesAndMethods.add(className);
@@ -884,7 +790,7 @@
             noParamMethod = noParamMethod.replaceAll("\\(.*", "");
 
             outFile.println("<div  class=\"rowdiv\"><a href=\"methodDependencies-"
-                + packageName
+                + filename(packageName)
                 + ".html#"
                 + noParamMethod
                 + "\">"
@@ -901,193 +807,16 @@
         outFile.close();
       }
     }
-
-    // finally, make the file for all the other fragments
-    String outFileName = "splitPointAllOtherFragmentsClasses.html";
-
-    final PrintWriter 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>Classes and methods in exclusives fragment for runAsync split point \"allOtherFragments\" </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(".tablediv {");
-    outFile.println("display:  table;");
-    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-collapse:separate;");
-    outFile.println("}");
-    outFile.println(".celldiv {");
-    outFile.println("float:left;/*fix for  buggy browsers*/");
-    outFile.println("display:  table-cell;");
-    outFile.println("width:49.5%;");
-    outFile.println("font-size: 14px;");
-    outFile.println("background-color:white;");
-    outFile.println("}");
-    outFile.println(".rowdiv  {");
-    outFile.println("display:  table-row;");
-    outFile.println("width:100%;");
-    outFile.println("}");
-    outFile.println("</style>");
-
-    outFile.println("<body>");
-    outFile.println("<center>");
-    outFile.println("<h2>Classes and methods in exclusives fragment for runAsync split point \"allOtherFragments\"</h2>");
-    outFile.println("</center>");
-    outFile.println("<hr>");
-
-    outFile.println("<div style=\"width:90%; height:80%; overflow-y:auto; overflow-x:auto; top: 30px; left:60px; position:relative; background-color:white\"");
-    outFile.println("<div  class=\"tablediv\">");
-
-    for (String classOrMethod : sortedClassesAndMethodsAllOtherFragments) {
-
-      // if it's a method
-      if ((GlobalInformation.displayDependencies == true)
-          && (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>");
-    outFile.println("</body>");
-    outFile.println("</html>");
-    outFile.close();
   }
 
-  public static void makeStringLiteralsClassesHtmls(
-      TreeMap<String, LiteralsCollection> nameToLitColl) throws IOException {
+  public static void makeStringLiteralsClassesTableHtmls(SizeBreakdown breakdown)
+      throws IOException {
+    Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl;
 
     for (String literalType : nameToLitColl.get("string").stringTypeToSize.keySet()) {
-
       String outFileName = literalType + "Strings.html";
-
-      final PrintWriter 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>Literals of type \"" + literalType + "\"</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(".tablediv {");
-      outFile.println("display:  table;");
-      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-collapse:separate;");
-      outFile.println("}");
-      outFile.println(".celldiv {");
-      outFile.println("float:left;/*fix for  buggy browsers*/");
-      outFile.println("display:  table-cell;");
-      outFile.println("width:49.5%;");
-      outFile.println("font-size: 14px;");
-      outFile.println("background-color:white;");
-      outFile.println("}");
-      outFile.println(".rowdiv  {");
-      outFile.println("display:  table-row;");
-      outFile.println("width:100%;");
-      outFile.println("}");
-      outFile.println("</style>");
-
-      outFile.println("<body>");
-      outFile.println("<center>");
-      outFile.println("<h2>Literals of type \"" + literalType + "\"</h2>");
-      outFile.println("</center>");
-      outFile.println("<hr>");
-
-      outFile.println("<div style=\"width:90%; height:80%; overflow-y:auto; overflow-x:auto; top: 30px; left:60px; position:relative; background-color:white\"");
-      outFile.println("<div  class=\"tablediv\">");
-
-      for (String literal : nameToLitColl.get("string").stringLiteralToType.keySet()) {
-
-        if (nameToLitColl.get("string").stringLiteralToType.get(literal).compareTo(
-            literalType) == 0) {
-
-          if (literal.trim().compareTo("") == 0) {
-            literal = "[whitespace only string]";
-          }
-
-          String newLiteral = "";
-          if (literal.length() > 100) {
-            int i;
-            for (i = 100; i < literal.length(); i = i + 100) {
-              String part1 = literal.substring(i - 100, i);
-              newLiteral = newLiteral + part1 + " ";
-            }
-            if (i - 100 > 0) {
-              newLiteral = newLiteral + literal.substring(i - 100);
-            }
-          } else {
-            newLiteral = literal;
-          }
-
-          String escliteral = escapeXml(newLiteral);
-          outFile.println("<div class=\"rowdiv\">");
-          outFile.println("<div  class=\"celldiv\">" + escliteral + "</div>");
-
-          for (String location : nameToLitColl.get("string").literalToLocations.get(literal)) {
-
-            String newLocation = "";
-            if (location.length() > 100) {
-              int i;
-              for (i = 100; i < location.length(); i = i + 100) {
-                String part1 = location.substring(i - 100, i);
-                newLocation = newLocation + part1 + " ";
-              }
-              if (i - 100 > 0) {
-                newLocation = newLocation + location.substring(i - 100);
-              }
-            } else {
-              newLocation = location;
-            }
-
-            outFile.println("<div  class=\"celldiv\">" + newLocation + "</div>");
-          }
-          outFile.println("</div>");
-        }
-      }
-      outFile.println("</div>");
-      outFile.println("</body>");
-      outFile.println("</html>");
-      outFile.close();
-    }
-  }
-
-  public static void makeStringLiteralsClassesTableHtmls(
-      TreeMap<String, LiteralsCollection> nameToLitColl) throws IOException {
-
-    for (String literalType : nameToLitColl.get("string").stringTypeToSize.keySet()) {
-
-      String outFileName = literalType + "Strings.html";
-      final PrintWriter outFile = new PrintWriter(outFileName);
+      final PrintWriter outFile = new PrintWriter(breakdown.getId() + "_"
+          + outFileName);
 
       outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
       outFile.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
@@ -1106,8 +835,8 @@
       outFile.println("<body>");
       outFile.println("<center>");
       outFile.println("<h2>Literals of type \"" + literalType + "\"</h2>");
+      addHeaderWithBreakdownContext(breakdown, outFile);
       outFile.println("</center>");
-      outFile.println("<hr>");
 
       outFile.println("<center>");
       outFile.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
@@ -1190,9 +919,32 @@
     }
   }
 
-  private static void makeCodeTypeHtml(String outFileName,
-      HashMap<String, CodeCollection> nameToCodeColl) throws IOException {
+  /**
+   * Adds a header line indicating which breakdown is being analyzed.
+   */
+  private static void addHeaderWithBreakdownContext(SizeBreakdown breakdown,
+      final PrintWriter outFile) {
+    outFile.println("<hr>");
+    outFile.println("<b>(Analyzing code subset: " + breakdown.getDescription()
+        + ")</b>");
+    outFile.println("<hr>");
+  }
 
+  /**
+   * Convert a potentially long string into a short file name. The current
+   * implementation simply hashes the long name.
+   */
+  private static String filename(String longFileName) {
+    try {
+      return Util.computeStrongName(longFileName.getBytes(Util.DEFAULT_ENCODING));
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private static String makeCodeTypeHtml(SizeBreakdown breakdown,
+      Map<String, CodeCollection> nameToCodeColl) throws IOException {
+    String outFileName = breakdown.getId() + "_codeTypeBreakdown.html";
     float maxSize = 0f;
     float sumSize = 0f;
     TreeMap<Float, String> sortedCodeTypes = new TreeMap<Float, String>(
@@ -1201,7 +953,7 @@
     // TODO(kprobst): turn this into a multimap?
     // com.google.common.collect.TreeMultimap
     for (String codeType : nameToCodeColl.keySet()) {
-      float curSize = nameToCodeColl.get(codeType).getCumPartialSize();
+      float curSize = nameToCodeColl.get(codeType).getCumPartialSize(breakdown);
       sumSize += curSize;
 
       if (curSize != 0f) {
@@ -1227,7 +979,8 @@
     for (Float size : sortedCodeTypes.keySet()) {
 
       String codeType = sortedCodeTypes.get(size);
-      String drillDownFileName = codeType + "Classes.html";
+      String drillDownFileName = breakdown.getId() + "_" + codeType
+          + "Classes.html";
 
       float ratio = (size / maxSize) * 79;
       float perc = (size / sumSize) * 100;
@@ -1263,29 +1016,75 @@
     outFile.println("</body>");
     outFile.println("</html>");
     outFile.close();
+
+    return outFileName;
   }
 
-  private static void makeFragmentsHtml(PrintWriter outFile) throws IOException {
-    int numSplitPoints = GlobalInformation.splitPointToLocation.size();
+  public static void makeTopLevelShell() throws IOException {
+    PrintWriter outFile = new PrintWriter("SoycDashboard-index.html");
+    
+    outFile.println("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">");
+    outFile.println("<html>");
+    outFile.println("<head>");
+    outFile.println("<title>Story of Your Compile - Top Level Dashboard for Permutation</title>");
 
+    outFile.println("<link rel=\"stylesheet\" href=\"roundedCorners.css\">");
+    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("</head>");
 
-    int outerHeight = 25 * (numSplitPoints + 1);
-    outFile.println("<div style=\"width:100%; margin:20px 0 20px 0; background-color:white;position:relative;height:"
-        + outerHeight + "\">");
-    float maxSize = 0;
-    for (int i = 0; i <= numSplitPoints; i++) {
-      maxSize += GlobalInformation.fragmentToPartialSize.get(i);
+    outFile.println("<body>");
+    outFile.println("<center>");
+    outFile.println("<h3>Story of Your Compile Dashboard</h3>");
+    outFile.println("<hr>");
+    if (GlobalInformation.fragmentToStories.size() > 1) {
+      outFile.println("<b>Initial download size: <span style=\"color:maroon\">"
+          + GlobalInformation.initialCodeBreakdown.sizeAllCode
+          + "</span></span></b>");
+    } else {
+      outFile.println("<b>Full code size: <span style=\"color:maroon\">"
+          + GlobalInformation.totalCodeBreakdown.sizeAllCode
+          + "</span></span></b>");
     }
 
-    int yOffset = 0;
-    for (int i = 0; i <= numSplitPoints; i++) {
-      String splitPointName = i == 0 ? "initialDownload"
-          : GlobalInformation.splitPointToLocation.get(i);
-      String drillDownFileName = "splitPoint" + splitPointName + "Classes.html";
-      String splitPointDescription = i == 0 ? "Initial download"
-          : ("Code exclusive to " + splitPointName);
+    outFile.println("<hr>");
+    
+    outFile.println("Available code subsets to analyze");
 
-      float size = GlobalInformation.fragmentToPartialSize.get(i);
+    int numSplitPoints = GlobalInformation.splitPointToLocation.size();
+
+    int outerHeight = 25 * (numSplitPoints + 2);
+    outFile.println("<div style=\"width:100%; margin:20px 0 20px 0; background-color:white;position:relative;height:"
+        + outerHeight + "\">");
+    float maxSize = GlobalInformation.totalCodeBreakdown.sizeAllCode;
+
+    int yOffset = 0;
+    for (int i = FRAGMENT_NUMBER_TOTAL_PROGRAM; i <= numSplitPoints; i++) {
+      String drillDownFileName, splitPointDescription;
+      if (i == FRAGMENT_NUMBER_TOTAL_PROGRAM) {
+        drillDownFileName = shellFileName(GlobalInformation.totalCodeBreakdown);
+        splitPointDescription = "Total program";
+      } else {
+        String splitPointName = i == FRAGMENT_NUMBER_INITIAL_DOWNLOAD ? "initialDownload"
+            : GlobalInformation.splitPointToLocation.get(i);
+        if (i == FRAGMENT_NUMBER_INITIAL_DOWNLOAD) {
+          drillDownFileName = shellFileName(GlobalInformation.initialCodeBreakdown);
+        } else {
+          drillDownFileName = "splitPoint-" + filename(splitPointName) + "-Classes.html";
+        }
+        splitPointDescription = i == FRAGMENT_NUMBER_INITIAL_DOWNLOAD ? "Initial download"
+            : ("Code exclusive to " + splitPointName);
+      }
+
+      float size;
+      if (i >= 0) {
+        size = GlobalInformation.fragmentToPartialSize.get(i);
+      } else {
+        size = GlobalInformation.totalCodeBreakdown.sizeAllCode;
+      }
       float ratio = (size / maxSize) * 79;
       if (ratio < 3) {
         ratio = 3;
@@ -1313,11 +1112,13 @@
       yOffset = yOffset + 25;
     }
     outFile.println("</div>");
+    outFile.println("</body></html>");
+    outFile.close();
   }
 
-  private static void makeLiteralsHtml(String outFileName,
-      TreeMap<String, LiteralsCollection> nameToLitColl) throws IOException {
-
+  private static String makeLiteralsHtml(SizeBreakdown breakdown,
+      Map<String, LiteralsCollection> nameToLitColl) throws IOException {
+    String outFileName = breakdown.getId() + "_literalsBreakdown.html";
     float maxSize = 0f;
     float sumSize = 0f;
     TreeMap<Float, String> sortedLitTypes = new TreeMap<Float, String>(
@@ -1351,7 +1152,8 @@
     for (Float size : sortedLitTypes.keySet()) {
 
       String literal = sortedLitTypes.get(size);
-      String drillDownFileName = literal + "Lits.html";
+      String drillDownFileName = breakdown.getId() + "_" + literal
+          + "Lits.html";
 
       float ratio = (size / maxSize) * 79;
       float perc = (size / sumSize) * 100;
@@ -1387,20 +1189,23 @@
     outFile.println("</body>");
     outFile.println("</html>");
     outFile.close();
+
+    return outFileName;
   }
 
-  private static void makePackageHtml(String outFileName) throws IOException {
-
+  private static String makePackageHtml(SizeBreakdown breakdown)
+      throws FileNotFoundException {
+    String outFileName = breakdown.getId() + "_" + "packageBreakdown.html";
+    Map<String, Float> packageToPartialSize = breakdown.packageToPartialSize;
     TreeMap<Float, String> sortedPackages = new TreeMap<Float, String>(
         Collections.reverseOrder());
     float maxSize = 0f;
     float sumSize = 0f;
-    for (String packageName : GlobalInformation.packageToPartialSize.keySet()) {
-      sortedPackages.put(
-          GlobalInformation.packageToPartialSize.get(packageName), packageName);
-      sumSize += GlobalInformation.packageToPartialSize.get(packageName);
-      if (GlobalInformation.packageToPartialSize.get(packageName) > maxSize) {
-        maxSize = GlobalInformation.packageToPartialSize.get(packageName);
+    for (String packageName : packageToPartialSize.keySet()) {
+      sortedPackages.put(packageToPartialSize.get(packageName), packageName);
+      sumSize += packageToPartialSize.get(packageName);
+      if (packageToPartialSize.get(packageName) > maxSize) {
+        maxSize = packageToPartialSize.get(packageName);
       }
     }
 
@@ -1418,9 +1223,8 @@
 
     int yOffset = 0;
     for (Float size : sortedPackages.keySet()) {
-
       String packageName = sortedPackages.get(size);
-      String drillDownFileName = packageName + "Classes.html";
+      String drillDownFileName = classesInPackageFileName(breakdown, packageName);
 
       float ratio = (size / maxSize) * 79;
 
@@ -1457,11 +1261,19 @@
     outFile.println("</body>");
     outFile.println("</html>");
     outFile.close();
+
+    return outFileName;
   }
 
-  private static void makeStringLiteralsHtml(String outFileName,
-      TreeMap<String, LiteralsCollection> nameToLitColl) throws IOException {
+  private static String classesInPackageFileName(SizeBreakdown breakdown,
+      String packageName) {
+    return breakdown.getId() + "_" + packageName
+        + "_Classes.html";
+  }
 
+  private static String makeStringLiteralsHtml(SizeBreakdown breakdown,
+      Map<String, LiteralsCollection> nameToLitColl) throws IOException {
+    String outFileName = breakdown.getId() + "_stringLiteralsBreakdown.html";
     final PrintWriter outFile = new PrintWriter(outFileName);
 
     outFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
@@ -1497,7 +1309,8 @@
       for (Float size : sortedStLitTypes.keySet()) {
 
         String stringLiteral = sortedStLitTypes.get(size);
-        String drillDownFileName = stringLiteral + "Strings.html";
+        String drillDownFileName = breakdown.getId() + "_" + stringLiteral
+            + "Strings.html";
 
         float ratio = (size / maxSize) * 79;
         float perc = (size / sumSize) * 100;
@@ -1537,5 +1350,11 @@
     outFile.println("</body>");
     outFile.println("</html>");
     outFile.close();
+
+    return outFileName;
+  }
+
+  private static String shellFileName(SizeBreakdown breakdown) {
+    return breakdown.getId() + "-overallBreakdown.html";
   }
 }
diff --git a/tools/soyc-vis/src/com/google/gwt/soyc/SizeBreakdown.java b/tools/soyc-vis/src/com/google/gwt/soyc/SizeBreakdown.java
new file mode 100644
index 0000000..1d0136a
--- /dev/null
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/SizeBreakdown.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2009 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.soyc;
+
+import com.google.gwt.dev.util.collect.HashSet;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * A size breakdown of one code collection.
+ */
+public class SizeBreakdown {
+  private static void initializeLiteralsCollection(
+      Map<String, LiteralsCollection> nameToLitColl) {
+    nameToLitColl.put("long", new LiteralsCollection("long"));
+    nameToLitColl.put("null", new LiteralsCollection("null"));
+    nameToLitColl.put("class", new LiteralsCollection("class"));
+    nameToLitColl.put("int", new LiteralsCollection("int"));
+    nameToLitColl.put("string", new LiteralsCollection("string"));
+    nameToLitColl.put("number", new LiteralsCollection("number"));
+    nameToLitColl.put("boolean", new LiteralsCollection("boolean"));
+    nameToLitColl.put("double", new LiteralsCollection("double"));
+    nameToLitColl.put("char", new LiteralsCollection("char"));
+    nameToLitColl.put("undefined", new LiteralsCollection("undefined"));
+    nameToLitColl.put("float", new LiteralsCollection("float"));
+  }
+
+  private static void initializeNameToCodeCollection(
+      HashMap<String, CodeCollection> nameToCodeColl) {
+    nameToCodeColl.put("allOther", new CodeCollection("allOther"));
+    nameToCodeColl.put("widget", new CodeCollection("widget"));
+    nameToCodeColl.put("rpcUser", new CodeCollection("rpcUser"));
+    nameToCodeColl.put("rpcGen", new CodeCollection("rpcGen"));
+    nameToCodeColl.put("rpcGwt", new CodeCollection("rpcGwt"));
+    nameToCodeColl.put("gwtLang", new CodeCollection("long"));
+    nameToCodeColl.put("jre", new CodeCollection("jre"));
+  }
+
+  public Map<String, Float> classToPartialSize = new HashMap<String, Float>();
+  public Map<String, Integer> classToSize = new HashMap<String, Integer>();
+  public Map<String, LiteralsCollection> nameToLitColl = new TreeMap<String, LiteralsCollection>();
+  public int nonAttributedBytes = 0;
+  public Set<String> nonAttributedStories = new HashSet<String>();;
+  public Map<String, Float> packageToPartialSize = new HashMap<String, Float>();
+  public Map<String, Integer> packageToSize = new HashMap<String, Integer>();
+  public HashMap<String, CodeCollection> nameToCodeColl = new HashMap<String, CodeCollection>();
+
+  public int sizeAllCode;
+
+  private final String description;
+  private final String id;
+
+  public SizeBreakdown(String description, String id) {
+    this.description = description;
+    this.id = id;
+
+    initializeLiteralsCollection(nameToLitColl);
+    initializeNameToCodeCollection(nameToCodeColl);
+  }
+
+  /**
+   * A short but human-readable description of this code collection.
+   */
+  public String getDescription() {
+    return description;
+  }
+
+  /**
+   * An identifier for this code collection suitable for use within file names.
+   */
+  public String getId() {
+    return id;
+  }
+}
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 4a2b57c..7fbf605 100644
--- a/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java
+++ b/tools/soyc-vis/src/com/google/gwt/soyc/SoycDashboard.java
@@ -26,8 +26,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.TreeSet;
@@ -43,6 +45,7 @@
 public class SoycDashboard {
 
   public static void main(String[] args) {
+    System.out.println("Generating the Story of Your Compile...");
     try {
       GlobalInformation.settings = Settings.fromArgumentList(args);
     } catch (Settings.ArgumentListException e) {
@@ -57,6 +60,8 @@
     GlobalInformation.displayDependencies = (settings.depFileName != null);
     GlobalInformation.displaySplitPoints = (settings.splitPointsFileName != null);
 
+    MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm = new MakeTopLevelHtmlForPerm();
+
     if (GlobalInformation.displayDependencies == true) {
       /**
        * handle dependencies
@@ -87,7 +92,7 @@
       }
 
       try {
-        MakeTopLevelHtmlForPerm.makeDependenciesHtml(dependencies);
+        makeTopLevelHtmlForPerm.makeDependenciesHtml(dependencies);
       } catch (IOException e) {
         throw new RuntimeException("Cannot open file. ", e);
       }
@@ -126,40 +131,8 @@
      * handle everything else
      */
 
-    // to store literals data
-    GlobalInformation.nameToLitColl.put("long", new LiteralsCollection("long"));
-    GlobalInformation.nameToLitColl.put("null", new LiteralsCollection("null"));
-    GlobalInformation.nameToLitColl.put("class",
-        new LiteralsCollection("class"));
-    GlobalInformation.nameToLitColl.put("int", new LiteralsCollection("int"));
-    GlobalInformation.nameToLitColl.put("string", new LiteralsCollection(
-        "string"));
-    GlobalInformation.nameToLitColl.put("number", new LiteralsCollection(
-        "number"));
-    GlobalInformation.nameToLitColl.put("boolean", new LiteralsCollection(
-        "boolean"));
-    GlobalInformation.nameToLitColl.put("double", new LiteralsCollection(
-        "double"));
-    GlobalInformation.nameToLitColl.put("char", new LiteralsCollection("char"));
-    GlobalInformation.nameToLitColl.put("undefined", new LiteralsCollection(
-        "undefined"));
-    GlobalInformation.nameToLitColl.put("float",
-        new LiteralsCollection("float"));
-
-    // to store code data
-    GlobalInformation.nameToCodeColl.put("allOther", new CodeCollection(
-        "allOther"));
-    GlobalInformation.nameToCodeColl.put("widget", new CodeCollection("widget"));
-    GlobalInformation.nameToCodeColl.put("rpcUser", new CodeCollection(
-        "rpcUser"));
-    GlobalInformation.nameToCodeColl.put("rpcGen", new CodeCollection("rpcGen"));
-    GlobalInformation.nameToCodeColl.put("rpcGwt", new CodeCollection("rpcGwt"));
-    GlobalInformation.nameToCodeColl.put("gwtLang", new CodeCollection("long"));
-    GlobalInformation.nameToCodeColl.put("jre", new CodeCollection("jre"));
-
     // make the parser handler
-    DefaultHandler handler = parseXMLDocument(GlobalInformation.nameToLitColl,
-        GlobalInformation.nameToCodeColl);
+    DefaultHandler handler = parseXMLDocument();
 
     // start parsing
     SAXParserFactory factoryMain = SAXParserFactory.newInstance();
@@ -183,18 +156,23 @@
     }
 
     // add to "All Other Code" if none of the special categories apply
-    updateAllOtherCodeType(GlobalInformation.nameToCodeColl);
+    for (SizeBreakdown breakdown : GlobalInformation.allSizeBreakdowns()) {
+      updateAllOtherCodeType(breakdown.nameToCodeColl);
+    }
 
     // now we need to aggregate numbers
     GlobalInformation.computePackageSizes();
     GlobalInformation.computePartialPackageSizes();
 
     // clean up the RPC categories
-    foldInRPCHeuristic(GlobalInformation.nameToCodeColl);
+    for (SizeBreakdown breakdown : GlobalInformation.allSizeBreakdowns()) {
+      foldInRPCHeuristic(breakdown.nameToCodeColl);
+    }
 
     // generate all the html files
-    makeHTMLFiles(GlobalInformation.nameToLitColl,
-        GlobalInformation.nameToCodeColl);
+    for (SizeBreakdown breakdown : GlobalInformation.allSizeBreakdowns()) {
+      makeHTMLFiles(makeTopLevelHtmlForPerm, breakdown);
+    }
 
     System.out.println("Finished creating reports. To see the dashboard, open SoycDashboard-index.html in your browser.");
   }
@@ -247,34 +225,28 @@
     }
   }
 
-  /*
+  /**
    * generates all the HTML files
    */
   private static void makeHTMLFiles(
-      final TreeMap<String, LiteralsCollection> nameToLitColl,
-      final HashMap<String, CodeCollection> nameToCodeColl) {
-
+      MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm, SizeBreakdown breakdown) {
     try {
-      MakeTopLevelHtmlForPerm.makePackageClassesHtmls();
-      MakeTopLevelHtmlForPerm.makeCodeTypeClassesHtmls(nameToCodeColl);
-      MakeTopLevelHtmlForPerm.makeLiteralsClassesTableHtmls(nameToLitColl);
-      MakeTopLevelHtmlForPerm.makeStringLiteralsClassesTableHtmls(nameToLitColl);
-      MakeTopLevelHtmlForPerm.makeSplitPointClassesHtmls();
-
-      // make the shell last so we can display aggregate information here
-      MakeTopLevelHtmlForPerm.makeHTMLShell(nameToCodeColl, nameToLitColl);
-
+      makeTopLevelHtmlForPerm.makePackageClassesHtmls(breakdown);
+      MakeTopLevelHtmlForPerm.makeCodeTypeClassesHtmls(breakdown);
+      MakeTopLevelHtmlForPerm.makeLiteralsClassesTableHtmls(breakdown);
+      MakeTopLevelHtmlForPerm.makeStringLiteralsClassesTableHtmls(breakdown);
+      makeTopLevelHtmlForPerm.makeSplitPointClassesHtmls();
+      MakeTopLevelHtmlForPerm.makeBreakdownShell(breakdown);
+      MakeTopLevelHtmlForPerm.makeTopLevelShell();
     } catch (IOException e) {
+      // TODO(spoon) pass all internal IOExceptions back to the top, so the
+      // give-up logic is in just one place
       throw new RuntimeException("Cannot open file. ", e);
     }
   }
 
-  private static DefaultHandler parseXMLDocument(
-      final TreeMap<String, LiteralsCollection> nameToLitColl,
-      final HashMap<String, CodeCollection> nameToCodeColl) {
-
+  private static DefaultHandler parseXMLDocument() {
     DefaultHandler handler = new DefaultHandler() {
-
       String curClassId;
       Integer curFragment;
       String curLineNumber;
@@ -284,7 +256,6 @@
       String curStoryId;
       String curStoryLiteralType;
       String curStoryRef;
-      boolean fragmentInLoadOrder = false;
       boolean specialCodeType = false;
       StringBuilder valueBuilder = new StringBuilder();
 
@@ -312,7 +283,7 @@
         if (strippedName.compareTo("storyref") == 0) {
           String value = valueBuilder.toString();
 
-          int numBytes = value.getBytes().length;
+          int numBytes = currentStorySize();
           if (curStoryRef != null) {
             if (!GlobalInformation.fragmentToPartialSize.containsKey(curFragment)) {
               GlobalInformation.fragmentToPartialSize.put(curFragment,
@@ -323,12 +294,8 @@
               GlobalInformation.fragmentToPartialSize.put(curFragment, newSize);
             }
 
-            // now do different things depending on whether this fragment is in
-            // the load order or not
-            if (fragmentInLoadOrder == false) {
-              GlobalInformation.allOtherFragmentsPartialSize += numBytes;
-            } else {
-              GlobalInformation.cumSizeAllCode += numBytes;
+            for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
+              breakdown.sizeAllCode += numBytes;
             }
 
             // add this size to the classes associated with it
@@ -339,66 +306,14 @@
                 GlobalInformation.numBytesDoubleCounted += numBytes;
               }
 
-              float partialSize = (float) numBytes
-                  / (float) GlobalInformation.storiesToCorrClasses.get(
-                      curStoryRef).size();
-
-              // now do different things depending on whether this fragment is
-              // in
-              // the load order or not
-              if (fragmentInLoadOrder == true) {
-                if ((!GlobalInformation.storiesToLitType.containsKey(curStoryRef))
-                    && (!GlobalInformation.storiesToCorrClasses.containsKey(curStoryRef))) {
-                  GlobalInformation.nonAttributedStories.add(curStoryRef);
-                  GlobalInformation.nonAttributedBytes += numBytes;
-                }
-
-                // go through all the classes for this story
-                for (String className : GlobalInformation.storiesToCorrClasses.get(curStoryRef)) {
-                  // get the corresponding package
-
-                  String packageName = "";
-
-                  if (!GlobalInformation.classToPackage.containsKey(className)) {
-                    // derive the package name from the class
-                    packageName = className;
-                    packageName = packageName.replaceAll("\\.[A-Z].*", "");
-                    GlobalInformation.classToPackage.put(className, packageName);
-                  } else {
-                    packageName = GlobalInformation.classToPackage.get(className);
-                  }
-                  parseClass(nameToCodeColl, className, packageName);
-
-                  if (!GlobalInformation.packageToClasses.containsKey(packageName)) {
-                    TreeSet<String> insertSet = new TreeSet<String>();
-                    insertSet.add(className);
-                    GlobalInformation.packageToClasses.put(packageName,
-                        insertSet);
-                  } else {
-                    GlobalInformation.packageToClasses.get(packageName).add(
-                        className);
-                  }
-
-                  if (GlobalInformation.classToSize.containsKey(className)) {
-                    int newSize = GlobalInformation.classToSize.get(className)
-                        + numBytes;
-                    GlobalInformation.classToSize.put(className, newSize);
-                  } else {
-                    GlobalInformation.classToSize.put(className, numBytes);
-                  }
-
-                  if (GlobalInformation.classToPartialSize.containsKey(className)) {
-                    float newSize = GlobalInformation.classToPartialSize.get(className)
-                        + partialSize;
-                    GlobalInformation.classToPartialSize.put(className, newSize);
-                  } else {
-                    GlobalInformation.classToPartialSize.put(className,
-                        partialSize);
-                  }
-                }
+              for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
+                accountForCurrentStory(breakdown.nameToCodeColl, breakdown);
               }
             }
-            updateLitTypes(nameToLitColl, value, numBytes);
+
+            for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
+              updateLitTypes(breakdown.nameToLitColl, value, numBytes);
+            }
           }
         }
       }
@@ -419,33 +334,101 @@
         if (strippedName.compareTo("story") == 0) {
           parseStory(attributes);
         } else if (strippedName.compareTo("of") == 0) {
-          parseOverrides(nameToCodeColl, attributes);
+          for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
+            parseOverrides(breakdown.nameToCodeColl, attributes);
+          }
         } else if (strippedName.compareTo("by") == 0) {
-          parseCorrelations(nameToCodeColl, attributes);
+          for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
+            parseCorrelations(breakdown.nameToCodeColl, attributes);
+          }
         } else if (strippedName.compareTo("origin") == 0) {
-          parseOrigins(nameToLitColl, attributes);
+          for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
+            parseOrigins(breakdown.nameToLitColl, attributes);
+          }
         } else if (strippedName.compareTo("js") == 0) {
           if (attributes.getValue("fragment") != null) {
-            // ignore all code that is not in the first load order
-
             curFragment = Integer.parseInt(attributes.getValue("fragment"));
-            if ((curFragment == 0)
-                || (curFragment == (GlobalInformation.numSplitPoints + 1))
-                || (curFragment == (GlobalInformation.numSplitPoints + 2))
-                || ((curFragment >= 2) && (curFragment <= GlobalInformation.numSplitPoints))) {
-              fragmentInLoadOrder = true;
-            } else {
-              fragmentInLoadOrder = false;
-            }
           } else {
-
             curFragment = -2;
           }
         } else if (strippedName.compareTo("storyref") == 0) {
-          parseJs(nameToLitColl, nameToCodeColl, attributes, curFragment);
+          for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
+            parseJs(breakdown.nameToLitColl, breakdown.nameToCodeColl,
+                attributes, curFragment);
+          }
         }
       }
 
+      private void accountForCurrentStory(
+          final HashMap<String, CodeCollection> nameToCodeColl,
+          SizeBreakdown breakdown) {
+        int storySize = currentStorySize();
+        if ((!GlobalInformation.storiesToLitType.containsKey(curStoryRef))
+            && (!GlobalInformation.storiesToCorrClasses.containsKey(curStoryRef))) {
+          breakdown.nonAttributedStories.add(curStoryRef);
+          breakdown.nonAttributedBytes += storySize;
+        }
+
+        // go through all the classes for this story
+        for (String className : GlobalInformation.storiesToCorrClasses.get(curStoryRef)) {
+          // get the corresponding package
+
+          String packageName = "";
+
+          if (!GlobalInformation.classToPackage.containsKey(className)) {
+            // derive the package name from the class
+            packageName = className;
+            packageName = packageName.replaceAll("\\.[A-Z].*", "");
+            GlobalInformation.classToPackage.put(className, packageName);
+          } else {
+            packageName = GlobalInformation.classToPackage.get(className);
+          }
+          parseClass(nameToCodeColl, className, packageName);
+
+          if (!GlobalInformation.packageToClasses.containsKey(packageName)) {
+            TreeSet<String> insertSet = new TreeSet<String>();
+            insertSet.add(className);
+            GlobalInformation.packageToClasses.put(packageName, insertSet);
+          } else {
+            GlobalInformation.packageToClasses.get(packageName).add(className);
+          }
+
+          if (breakdown.classToSize.containsKey(className)) {
+            int newSize = breakdown.classToSize.get(className) + storySize;
+            breakdown.classToSize.put(className, newSize);
+          } else {
+            breakdown.classToSize.put(className, storySize);
+          }
+
+          if (breakdown.classToPartialSize.containsKey(className)) {
+            float newSize = breakdown.classToPartialSize.get(className)
+                + currentStoryPartialSize();
+            breakdown.classToPartialSize.put(className, newSize);
+          } else {
+            breakdown.classToPartialSize.put(className,
+                currentStoryPartialSize());
+          }
+        }
+      }
+
+      private Collection<SizeBreakdown> breakdownsForCurFragment() {
+        List<SizeBreakdown> breakdowns = new ArrayList<SizeBreakdown>();
+        breakdowns.add(GlobalInformation.totalCodeBreakdown);
+        if (curFragment == 0) {
+          breakdowns.add(GlobalInformation.initialCodeBreakdown);
+        }
+        return breakdowns;
+      }
+
+      private float currentStoryPartialSize() {
+        return (float) currentStorySize()
+            / (float) GlobalInformation.storiesToCorrClasses.get(curStoryRef).size();
+      }
+
+      private int currentStorySize() {
+        return valueBuilder.toString().getBytes().length;
+      }
+
       /*
        * parses the "class" portion of the XML file
        */
@@ -518,8 +501,7 @@
       /*
        * parses the "JS" portion of the XML file
        */
-      private void parseJs(
-          final TreeMap<String, LiteralsCollection> nameToLitColl,
+      private void parseJs(final Map<String, LiteralsCollection> nameToLitColl,
           final HashMap<String, CodeCollection> nameToCodeColl,
           Attributes attributes, Integer curFragment) {
         curRelevantLitTypes.clear();
@@ -566,7 +548,7 @@
        * parses the "origins" portion of the XML file
        */
       private void parseOrigins(
-          final TreeMap<String, LiteralsCollection> nameToLitColl,
+          final Map<String, LiteralsCollection> nameToLitColl,
           Attributes attributes) {
         if ((curStoryLiteralType.compareTo("") != 0)
             && (attributes.getValue("lineNumber") != null)
@@ -623,13 +605,13 @@
             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);
+            for (SizeBreakdown breakdown : breakdownsForCurFragment()) {
+              if (!breakdown.nameToLitColl.get(curStoryLiteralType).storyToLocations.containsKey(curStoryId)) {
+                HashSet<String> insertSet = new HashSet<String>();
+                breakdown.nameToLitColl.get(curStoryLiteralType).storyToLocations.put(
+                    curStoryId, insertSet);
+              }
             }
-
           } else {
             curStoryLiteralType = "";
           }
@@ -640,8 +622,8 @@
        * This method assigns strings to the appropriate category
        */
       private void updateLitTypes(
-          final TreeMap<String, LiteralsCollection> nameToLitColl,
-          String value, int numBytes) {
+          final Map<String, LiteralsCollection> nameToLitColl, String value,
+          int numBytes) {
 
         int iNumCounted = 0;
 
@@ -722,9 +704,7 @@
                     insertSet);
               }
             }
-          }
-
-          else {
+          } else {
             // note that this will double-count (i.e., it will count a literal
             // twice if it's in the output twice), as it should.
             nameToLitColl.get(relLitType).cumSize += numBytes;