This patch moves SOYC's source code underneath dev/core/src.  It leaves
gwt-soyc-vis.jar existing and containing the SOYC static resources (css and
gifs).  It has gwt-dev-platform.jar also contain the SOYC static resources.

After this patch, any existing build rules and scripts should be able
to replace references to gwt-soyc-vis.jar by references to gwt-dev-platform.jar.

Review by: fabbott


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5993 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/common.ant.xml b/dev/common.ant.xml
index ef64c7a..10d9500 100755
--- a/dev/common.ant.xml
+++ b/dev/common.ant.xml
@@ -4,6 +4,7 @@
 
   <property.ensure name="gwt.core.root" location="../core" />
   <property.ensure name="gwt.core.build" location="${project.build}/../core" />
+  <property.ensure name="gwt.tools.soyc" location="${gwt.root}/tools/soyc-vis" />
 
   <target name="compile" description="Compile all java files">
     <mkdir dir="${javac.out}" />
@@ -30,6 +31,9 @@
         <fileset dir="${gwt.core.build}/bin" />
         <fileset file="${gwt.tools.lib}/eclipse/${gwt.dev.swt.jar}" />
         <fileset file="${gwt.core.build}/alldeps.jar" />
+        <fileset dir="${gwt.tools.soyc}/images"/>
+        <fileset file="${gwt.tools.soyc}/classLevel.css"/>
+        <fileset file="${gwt.tools.soyc}/roundedCorners.css"/>
       </sourcefiles>
       <targetfiles>
         <fileset file="${project.lib}"/>
@@ -46,6 +50,11 @@
           <fileset dir="${gwt.core.build}/bin" />
           <zipfileset src="${gwt.tools.lib}/eclipse/${gwt.dev.swt.jar}" />
           <zipfileset src="${gwt.core.build}/alldeps.jar" />
+
+          <zipfileset dir="${gwt.tools.soyc}/images" prefix="com/google/gwt/soyc/resources/images"/>
+          <zipfileset file="${gwt.tools.soyc}/classLevel.css" prefix="com/google/gwt/soyc/resources/"/>
+          <zipfileset file="${gwt.tools.soyc}/roundedCorners.css" prefix="com/google/gwt/soyc/resources/"/>
+
           <manifest>
             <attribute name="Main-Class" value="com.google.gwt.dev.GWTMain" />
           </manifest>
diff --git a/dev/core/src/com/google/gwt/soyc/CodeCollection.java b/dev/core/src/com/google/gwt/soyc/CodeCollection.java
new file mode 100644
index 0000000..3b9b451
--- /dev/null
+++ b/dev/core/src/com/google/gwt/soyc/CodeCollection.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2008 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 java.util.TreeSet;
+
+/**
+ * Holds a set of all classes in a SOYC report, and can calculate size summaries
+ * for them.
+ */
+public class CodeCollection {
+
+  public TreeSet<String> classes = new TreeSet<String>();
+  public float cumPartialSize = 0f;
+  public int cumSize = 0;
+  public TreeSet<String> stories = new TreeSet<String>();
+
+  public float getCumPartialSize(SizeBreakdown breakdown) {
+    cumPartialSize = 0f;
+    for (String className : classes) {
+      if (breakdown.classToSize.containsKey(className)) {
+        cumPartialSize += breakdown.classToSize.get(className);
+      }
+    }
+    return cumPartialSize;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/soyc/GlobalInformation.java b/dev/core/src/com/google/gwt/soyc/GlobalInformation.java
new file mode 100644
index 0000000..aaf9edf
--- /dev/null
+++ b/dev/core/src/com/google/gwt/soyc/GlobalInformation.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2008 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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * Information global to the entire SOYC report generator.
+ */
+public class GlobalInformation {
+  private static final SizeBreakdown[] EMPTY_SIZE_BREAKDOWN = new SizeBreakdown[0];
+  private HashMap<String, String> classToPackage = new HashMap<String, String>();
+  private HashMap<String, HashSet<String>> classToWhatItDependsOn = new HashMap<String, HashSet<String>>();
+  private Map<Integer, SizeBreakdown> exclusiveCodeBreakdowns = new HashMap<Integer, SizeBreakdown>();
+  private SizeBreakdown initialCodeBreakdown = new SizeBreakdown(
+      "Initially downloaded code", "initial");
+  private SizeBreakdown leftoversBreakdown = new SizeBreakdown(
+      "Leftovers code, code not in any other category", "leftovers");
+  private int numSplitPoints = 0;
+  private Map<String, TreeSet<String>> packageToClasses = new TreeMap<String, TreeSet<String>>();
+  private ArrayList<Integer> splitPointInitialLoadSequence = new ArrayList<Integer>();
+  private HashMap<Integer, String> splitPointToLocation = new HashMap<Integer, String>();
+  private SizeBreakdown totalCodeBreakdown = new SizeBreakdown("Total program",
+      "total");
+
+  public SizeBreakdown[] allSizeBreakdowns() {
+    List<SizeBreakdown> breakdowns = new ArrayList<SizeBreakdown>();
+    breakdowns.add(totalCodeBreakdown);
+    breakdowns.add(initialCodeBreakdown);
+    if (numSplitPoints > 0) {
+      breakdowns.add(leftoversBreakdown);
+      for (int sp = 1; sp <= numSplitPoints; sp++) {
+        breakdowns.add(splitPointCodeBreakdown(sp));
+      }
+    }
+    return breakdowns.toArray(EMPTY_SIZE_BREAKDOWN);
+  }
+
+  /**
+   * Computes all package sizes.
+   */
+  public void computePackageSizes() {
+    for (SizeBreakdown breakdown : allSizeBreakdowns()) {
+      computePackageSizes(breakdown.packageToSize, breakdown.classToSize);
+    }
+  }
+
+  /**
+   * Gets the mapping from each class to its package.
+   * 
+   * @return classToPackage
+   */
+  public final HashMap<String, String> getClassToPackage() {
+    return classToPackage;
+  }
+
+  /**
+   * Gets the mapping from a class to what it depends on.
+   * 
+   * @return classToWhatItDependsOn
+   */
+  public final HashMap<String, HashSet<String>> getClassToWhatItDependsOn() {
+    return classToWhatItDependsOn;
+  }
+
+  /**
+   * Gets the exclusive code breakdown.
+   * 
+   * @return exclusiveCodeBreakdown
+   */
+  public final Map<Integer, SizeBreakdown> getExclusiveCodeBreakdowns() {
+    return exclusiveCodeBreakdowns;
+  }
+
+  /**
+   * Gets the initial code breakdown.
+   * 
+   * @return initialCodeBreakdown
+   */
+  public final SizeBreakdown getInitialCodeBreakdown() {
+    return initialCodeBreakdown;
+  }
+
+  /**
+   * Gets the leftovers code breakdown.
+   * 
+   * @return leftoversCodeBreakdown
+   */
+  public final SizeBreakdown getLeftoversBreakdown() {
+    return leftoversBreakdown;
+  }
+
+  /**
+   * Gets the number of split points.
+   * 
+   * @return numSplitPoints
+   */
+  public final int getNumSplitPoints() {
+    return numSplitPoints;
+  }
+
+  /**
+   * Gets the mapping from packages to classes.
+   * 
+   * @return packageToClasses
+   */
+  public final Map<String, TreeSet<String>> getPackageToClasses() {
+    return packageToClasses;
+  }
+
+  /**
+   * Gets the initial load sequence.
+   * 
+   * @return splitPointInitialLoadSequence
+   */
+  public final ArrayList<Integer> getSplitPointInitialLoadSequence() {
+    return splitPointInitialLoadSequence;
+  }
+
+  /**
+   * Gets the mapping from split points to locations where they were set.
+   * 
+   * @return splitPointToLocation
+   */
+  public final HashMap<Integer, String> getSplitPointToLocation() {
+    return splitPointToLocation;
+  }
+
+  /**
+   * Gets the total code breakdown.
+   * 
+   * @return totalCodeBreakdown
+   */
+  public final SizeBreakdown getTotalCodeBreakdown() {
+    return totalCodeBreakdown;
+  }
+
+  /**
+   * Increments the split point count.
+   */
+  public final void incrementSplitPoints() {
+    numSplitPoints++;
+  }
+
+  /**
+   * Gets an exclusive code breakdown for a split point.
+   * 
+   * @param sp split point
+   * @return exlusive code breakdown for sp
+   */
+  public SizeBreakdown splitPointCodeBreakdown(int sp) {
+    assert sp >= 1 && sp <= numSplitPoints;
+    if (!exclusiveCodeBreakdowns.containsKey(sp)) {
+      exclusiveCodeBreakdowns.put(sp, new SizeBreakdown("split point " + sp
+          + ": " + splitPointToLocation.get(sp), "sp" + sp));
+    }
+    return exclusiveCodeBreakdowns.get(sp);
+  }
+
+  /**
+   * Computes package sizes from class sizes. TODO(spoon) move this to the
+   * SizeBreakdown class.
+   * 
+   * @param packageToSize mapping from packages to their sizes
+   * @param classToSize mapping from classes to their sizes
+   */
+  private 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)) {
+          int curSize = classToSize.get(className);
+          int newSize = curSize + packageToSize.get(packageName);
+          packageToSize.put(packageName, newSize);
+        }
+      }
+    }
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/soyc/LiteralsCollection.java b/dev/core/src/com/google/gwt/soyc/LiteralsCollection.java
new file mode 100644
index 0000000..18f2747
--- /dev/null
+++ b/dev/core/src/com/google/gwt/soyc/LiteralsCollection.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2008 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 java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * A collection of literals.
+ */
+public class LiteralsCollection {
+  public int size = 0;
+  public final Set<String> literals = new TreeSet<String>();
+}
diff --git a/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
new file mode 100644
index 0000000..4ff9124
--- /dev/null
+++ b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
@@ -0,0 +1,1425 @@
+/*
+ * Copyright 2008 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.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;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A utility to make the top level HTTML file for one permutation.
+ */
+public class MakeTopLevelHtmlForPerm {
+  /**
+   * A dependency linker for exclusive fragments. It links to nothing.
+   */
+  public class DependencyLinkerForExclusiveFragment implements DependencyLinker {
+    public String dependencyLinkForClass(String className, String permutationId) {
+      return null;
+    }
+  }
+
+  /**
+   * A dependency linker for the initial code download. It links to the
+   * dependencies for the initial download.
+   */
+  public class DependencyLinkerForInitialCode implements DependencyLinker {
+    public String dependencyLinkForClass(String className, String permutationId) {
+      String packageName = globalInformation.getClassToPackage().get(className);
+      assert packageName != null;
+      return dependenciesFileName("initial", packageName, permutationId) + "#"
+          + className;
+    }
+  }
+
+  /**
+   * A dependency linker for the leftovers fragment. It links to leftovers
+   * status pages.
+   */
+  public class DependencyLinkerForLeftoversFragment implements DependencyLinker {
+    public String dependencyLinkForClass(String className, String permutationId) {
+      return leftoversStatusFileName(className, permutationId);
+    }
+  }
+
+  /**
+   * A dependency linker for the total program breakdown. It links to a split
+   * status page.
+   * 
+   */
+  public class DependencyLinkerForTotalBreakdown implements DependencyLinker {
+    public String dependencyLinkForClass(String className, String permutationId) {
+      return splitStatusFileName(className, permutationId);
+    }
+  }
+
+  interface DependencyLinker {
+    String dependencyLinkForClass(String className, String permutationId);
+  }
+
+  /**
+   * 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;
+
+  /**
+   * A pattern describing the name of dependency graphs for code fragments
+   * corresponding to a specific split point. These can be either exclusive
+   * fragments or fragments of code for split points in the initial load
+   * sequence.
+   */
+  private static final Pattern PATTERN_SP_INT = Pattern.compile("sp([0-9]+)");
+
+  private static String RESOURCES_PATH = MakeTopLevelHtmlForPerm.class.getPackage().getName().replace(
+      '.', '/')
+      + "/resources/";
+
+  public static String escapeXml(String unescaped) {
+    String escaped = unescaped.replaceAll("\\&", "&amp;");
+    escaped = escaped.replaceAll("\\<", "&lt;");
+    escaped = escaped.replaceAll("\\>", "&gt;");
+    escaped = escaped.replaceAll("\\\"", "&quot;");
+    escaped = escaped.replaceAll("\\'", "&apos;");
+    return escaped;
+  }
+
+  private static void addCenteredHeader(final PrintWriter outFile, String header) {
+    outFile.println("<hr>");
+    outFile.println("<b>" + header + "</b>");
+    outFile.println("<hr>");
+  }
+
+  /**
+   * Adds a header line indicating which breakdown is being analyzed.
+   */
+  private static void addHeaderWithBreakdownContext(SizeBreakdown breakdown,
+      final PrintWriter outFile) {
+    addCenteredHeader(outFile, headerLineForBreakdown(breakdown));
+  }
+
+  private static String classesInPackageFileName(SizeBreakdown breakdown,
+      String packageName, String permutationId) {
+    return breakdown.getId() + "_" + packageName + "-" + permutationId
+        + "_Classes.html";
+  }
+
+  /**
+   * 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 headerLineForBreakdown(SizeBreakdown breakdown) {
+    return "(Analyzing code subset: " + breakdown.getDescription() + ")";
+  }
+
+  private static String shellFileName(SizeBreakdown breakdown,
+      String permutationId) {
+    return breakdown.getId() + "-" + permutationId + "-overallBreakdown.html";
+  }
+
+  /**
+   * Global information for this permutation.
+   */
+  private GlobalInformation globalInformation = new GlobalInformation();
+
+  /**
+   * Settings for this permutation.
+   */
+  private Settings settings = new Settings();
+
+  /**
+   * Default constructor. Will be used for all permutations.
+   */
+  MakeTopLevelHtmlForPerm() {
+    this.globalInformation = new GlobalInformation();
+    this.settings = new Settings();
+  }
+
+  /**
+   * Constructor for a specific permutation.
+   * 
+   * @param globalInformation All the information about this permutation
+   */
+  MakeTopLevelHtmlForPerm(final GlobalInformation globalInformation) {
+    this.globalInformation = globalInformation;
+  }
+
+  public 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]), classPath, inputFileName, isDirectory);
+      }
+    } else {
+      if (!srcPath.exists()) {
+        copyFileOrDirectoryFromJar(classPath, inputFileName, dstPath,
+            isDirectory);
+      } else {
+        InputStream in = new FileInputStream(srcPath);
+        OutputStream out = new FileOutputStream(dstPath);
+        // Transfer bytes from in to out
+        byte[] buf = new byte[1024];
+        int len;
+        while ((len = in.read(buf)) > 0) {
+          out.write(buf, 0, len);
+        }
+        in.close();
+        out.close();
+      }
+    }
+  }
+
+  public 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 = getOutFile(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 GlobalInformation getGlobalInformation() {
+    return globalInformation;
+  }
+
+  public void makeBreakdownShell(SizeBreakdown breakdown, String permutationId)
+      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 = settings.resources.get();
+    if (classPath == null) {
+      classPath = System.getProperty("java.class.path");
+    }
+    if (!classPath.endsWith("/")) {
+      classPath += "/";
+    }
+    String inputFileName = "roundedCorners.css";
+    File inputFile = new File(classPath + RESOURCES_PATH + inputFileName);
+    File outputFile = getOutFile("roundedCorners.css");
+    copyFileOrDirectory(inputFile, outputFile, classPath, RESOURCES_PATH
+        + inputFileName, false);
+
+    inputFileName = "classLevel.css";
+    File inputFile2 = new File(classPath + RESOURCES_PATH + inputFileName);
+    File outputFile2 = getOutFile("classLevel.css");
+    copyFileOrDirectory(inputFile2, outputFile2, classPath, RESOURCES_PATH
+        + inputFileName, false);
+
+    inputFileName = "common.css";
+    File inputFile3 = new File(classPath + RESOURCES_PATH + inputFileName);
+    File outputFile3 = getOutFile("common.css");
+    copyFileOrDirectory(inputFile3, outputFile3, classPath, RESOURCES_PATH
+        + inputFileName, false);
+
+    inputFileName = "images";
+    File inputDir = new File(classPath + RESOURCES_PATH + "images");
+    File outputDir = getOutFile("images");
+    copyFileOrDirectory(inputDir, outputDir, classPath, inputFileName, true);
+
+    final PrintWriter outFile = new PrintWriter(getOutFile(shellFileName(
+        breakdown, permutationId)));
+
+    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=\"common.css\">");
+    outFile.println("  <style type=\"text/css\">");
+    outFile.println(" body {");
+    outFile.println("   background-color: #728FCE;");
+    outFile.println(" }");
+
+    outFile.println(".abs {");
+    outFile.println("  position: absolute;");
+    outFile.println(" overflow: hidden;");
+    outFile.println(" }");
+
+    outFile.println(" .mainHeader {");
+    outFile.println("   left:0; right: 0;");
+    outFile.println("   top:0; height: 6em;");
+    outFile.println("  text-align: center;");
+    outFile.println("}");
+
+    outFile.println(".mainContent {");
+    outFile.println("  left:0; right: 0;");
+    outFile.println("  top: 6em; bottom: 0;");
+    outFile.println("}");
+
+    outFile.println(".header {");
+    outFile.println("  left:0; right: 0;");
+    outFile.println(" top:0; height: 5em;");
+    outFile.println("  padding: 0.5em;");
+    outFile.println("}");
+
+    outFile.println(".innerContent {");
+    outFile.println("   left:0; right: 0;");
+    outFile.println("  top: 2em; bottom: 0;");
+    outFile.println("}");
+
+    outFile.println(".frame {");
+    outFile.println(" width: 100%; height: 100%;");
+    outFile.println("  border: 0px;");
+    outFile.println("}");
+
+    outFile.println(".packages {");
+    outFile.println(" left:0; right: 0;");
+    outFile.println(" top:0; bottom: 50%;");
+    outFile.println("}");
+
+    outFile.println(".codeType {");
+    outFile.println("left:0; right: 0;");
+    outFile.println("top:50%; bottom: 0;");
+    outFile.println("}");
+    outFile.println("</style>");
+    outFile.println("</head>");
+
+    outFile.println("<body>");
+    outFile.println("<div class='abs mainHeader'>");
+
+    outFile.println("<center>");
+    outFile.println("<h3>Story of Your Compile Dashboard</h3>");
+
+    addHeaderWithBreakdownContext(breakdown, outFile);
+
+    outFile.println("</center>");
+
+    outFile.println("<hr>");
+    outFile.println("</div>");
+
+    outFile.println("<div class='abs mainContent' style='overflow:auto'>");
+    outFile.println("<div class='abs packages'>");
+
+    outFile.println("<div class='abs header'>Package breakdown</div>");
+    outFile.println("<div class='abs innerContent'>");
+    String packageBreakdownFileName = makePackageHtml(breakdown, permutationId);
+    outFile.println(" <iframe class='frame' src=\"" + packageBreakdownFileName
+        + "\" scrolling=auto></iframe>");
+    outFile.println("</div>");
+    outFile.println("</div>");
+
+    outFile.println("<div class='abs codeType'>");
+    outFile.println("<div class='abs header'>Code type breakdown</div>");
+
+    outFile.println("<div class='abs innerContent'>");
+    String codeTypeBreakdownFileName = makeCodeTypeHtml(breakdown,
+        nameToCodeColl, nameToLitColl, permutationId);
+    outFile.println("<iframe class='frame' src=\"" + codeTypeBreakdownFileName
+        + "\" scrolling=auto></iframe>");
+    outFile.println("</div>");
+    outFile.println("</div>");
+    outFile.println("</div>");
+    outFile.println("</body>");
+    outFile.println("</html>");
+    outFile.close();
+  }
+
+  public void makeCodeTypeClassesHtmls(SizeBreakdown breakdown,
+      String permutationId) throws IOException {
+    HashMap<String, CodeCollection> nameToCodeColl = breakdown.nameToCodeColl;
+
+    for (String codeType : nameToCodeColl.keySet()) {
+
+      // construct file name
+      String outFileName = breakdown.getId() + "_" + codeType + "-"
+          + permutationId + "Classes.html";
+
+      float maxSize = 0f;
+      TreeMap<Float, String> sortedClasses = new TreeMap<Float, String>(
+          Collections.reverseOrder());
+
+      for (String className : nameToCodeColl.get(codeType).classes) {
+        if (breakdown.classToSize.containsKey(className)) {
+
+          float curSize = 0f;
+          if (breakdown.classToSize.containsKey(className)) {
+            curSize = breakdown.classToSize.get(className);
+          }
+
+          if (curSize != 0f) {
+            sortedClasses.put(curSize, className);
+            if (curSize > maxSize) {
+              maxSize = curSize;
+            }
+          }
+        }
+      }
+
+      final PrintWriter outFile = new PrintWriter(getOutFile(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("<title>Classes in package \"" + codeType + "\"</title>");
+      outFile.println("  <meta http-equiv=\"content-type\" content=\"text/html;charset=ISO-8859-1\">");
+      outFile.println("  <link rel=\"stylesheet\" href=\"common.css\" media=\"screen\">");
+
+      outFile.println("  <style type=\"text/css\">");
+
+      outFile.println(".abs {");
+      outFile.println("  position: absolute;");
+      outFile.println(" overflow: hidden;");
+      outFile.println(" }");
+
+      outFile.println(" .mainHeader {");
+      outFile.println("   left:0; right: 0;");
+      outFile.println("   top:0; height: 6em;");
+      outFile.println("  text-align: center;");
+      outFile.println("background-color: #728FCE;");
+      outFile.println("}");
+
+      outFile.println(".mainContent {");
+      outFile.println("  left:0; right: 0;");
+      outFile.println("  top: 6em; bottom: 0;");
+      outFile.println("}");
+
+      outFile.println(".header {");
+      outFile.println("  left:0; right: 0;");
+      outFile.println(" top:0; height: 2em;");
+      outFile.println("  padding: 0.5em;");
+      outFile.println("}");
+
+      outFile.println(".innerContent {");
+      outFile.println("   left:0; right: 0;");
+      outFile.println("  top: 2em; bottom: 0;");
+      outFile.println("}");
+
+      outFile.println(".frame {");
+      outFile.println(" width: 100%; height: 100%;");
+      outFile.println("  border: 0px;");
+      outFile.println("}");
+
+      outFile.println(".packages {");
+      outFile.println(" left:0; right: 0;");
+      outFile.println(" top:0; bottom: 50%;");
+      outFile.println("}");
+
+      outFile.println(".codeType {");
+      outFile.println("left:0; right: 0;");
+      outFile.println("top:50%; bottom: 0;");
+      outFile.println("}");
+      outFile.println("</style>");
+      outFile.println("</head>");
+
+      outFile.println("<body>");
+      outFile.println("<div class='abs mainHeader'>");
+      outFile.println("<h2>Classes in package \"" + codeType + "\"</h2>");
+      addHeaderWithBreakdownContext(breakdown, outFile);
+      outFile.println("</center>");
+      outFile.println("<hr>");
+      outFile.println("</div>");
+      outFile.println("<div class='abs mainContent' style='overflow:auto'>");
+      outFile.println("<table style='width:100%'>");
+      outFile.println("<thead>");
+      outFile.println("<th class='barlabel'>Size</th>");
+      outFile.println("<th class='barlabel'></th>");
+      outFile.println("<th style='width:100%' class='barlabel'></th>");
+      outFile.println("</thead>");
+
+      for (Float size : sortedClasses.keySet()) {
+
+        String className = sortedClasses.get(size);
+
+        float ratio = (size / maxSize) * 85;
+
+        if (ratio < 5) {
+          ratio = 5;
+        }
+
+        outFile.println("<tr>");
+        outFile.println("<td class=\"barlabel\">" + size + "</td>");
+        outFile.println("<td class=\"barlabel\">" + className + "</td>");
+        outFile.println("<td class=\"box\">");
+        outFile.println("<div style=\"width:"
+            + ratio
+            + "%;\" class=\"lb\"><div class=\"rb\"><div class=\"bb\"><div class=\"blc\"><div class=\"brc\"><div class=\"tb\"><div class=\"tlc\"><div class=\"trc\"><div class=\"content\"></div></div></div></div></div></div></div></div>");
+        outFile.println("</td>");
+        outFile.println("</tr>");
+      }
+
+      addStandardHtmlEnding(outFile);
+      outFile.close();
+    }
+  }
+
+  public void makeDependenciesHtml(
+      Map<String, Map<String, String>> allDependencies, String permutationId)
+      throws IOException {
+    for (String depGraphName : allDependencies.keySet()) {
+      makeDependenciesHtml(depGraphName, allDependencies.get(depGraphName),
+          permutationId);
+    }
+  }
+
+  public void makeLeftoverStatusPages(String permutationId) throws IOException {
+    for (String className : globalInformation.getClassToPackage().keySet()) {
+      makeLeftoversStatusPage(className, permutationId);
+    }
+  }
+
+  public void makeLiteralsClassesTableHtmls(SizeBreakdown breakdown,
+      String permutationId) throws IOException {
+    Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl;
+
+    for (String literalType : nameToLitColl.keySet()) {
+
+      String outFileName = literalType + "-" + permutationId + "Lits.html";
+      final PrintWriter outFile = new PrintWriter(getOutFile(breakdown.getId()
+          + "_" + 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("</style>");
+
+      outFile.println("<body>");
+      outFile.println("<center>");
+      outFile.println("<h2>Literals of type \"" + literalType + "\"</h2>");
+      addHeaderWithBreakdownContext(breakdown, outFile);
+      outFile.println("</center>");
+
+      outFile.println("<center>");
+      outFile.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
+
+      for (String literal : nameToLitColl.get(literalType).literals) {
+        String newLiteral = "";
+        if (literal.length() > 80) {
+          int i;
+          for (i = 80; i < literal.length(); i = i + 80) {
+            String part1 = literal.substring(i - 80, i);
+            newLiteral = newLiteral + part1 + " ";
+          }
+          if (i - 80 > 0) {
+            newLiteral = newLiteral + literal.substring(i - 80);
+          }
+        } else {
+          newLiteral = literal;
+        }
+
+        if (newLiteral.trim().length() == 0) {
+          newLiteral = "[whitespace only string]";
+        }
+        String escliteral = escapeXml(newLiteral);
+
+        outFile.println("<tr>");
+        outFile.println("<td>" + escliteral + "</td>");
+        outFile.println("</tr>");
+      }
+
+      outFile.println("</table>");
+      outFile.println("<center>");
+
+      addStandardHtmlEnding(outFile);
+      outFile.close();
+    }
+  }
+
+  /**
+   * Make size breakdowns for each package for one code collection.
+   */
+  public void makePackageClassesHtmls(SizeBreakdown breakdown,
+      DependencyLinker depLinker, String permutationId) throws IOException {
+    for (String packageName : globalInformation.getPackageToClasses().keySet()) {
+
+      TreeMap<Float, String> sortedClasses = new TreeMap<Float, String>(
+          Collections.reverseOrder());
+      float maxSize = 0f;
+      float sumSize = 0f;
+      for (String className : globalInformation.getPackageToClasses().get(
+          packageName)) {
+        float curSize = 0f;
+        if (!breakdown.classToSize.containsKey(className)) {
+          // This class not present in this code collection
+        } else {
+          curSize = breakdown.classToSize.get(className);
+        }
+
+        if (curSize != 0f) {
+
+          sumSize += curSize;
+          sortedClasses.put(curSize, className);
+          if (curSize > maxSize) {
+            maxSize = curSize;
+          }
+        }
+      }
+
+      PrintWriter outFile = new PrintWriter(
+          getOutFile(classesInPackageFileName(breakdown, packageName,
+              permutationId)));
+
+      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("<title>Classes in package \"" + packageName
+          + "\"</title>");
+      outFile.println("  <meta http-equiv=\"content-type\" content=\"text/html;charset=ISO-8859-1\">");
+      outFile.println("  <link rel=\"stylesheet\" href=\"common.css\" media=\"screen\">");
+
+      outFile.println("  <style type=\"text/css\">");
+
+      outFile.println(".abs {");
+      outFile.println("  position: absolute;");
+      outFile.println(" overflow: hidden;");
+      outFile.println(" }");
+
+      outFile.println(" .mainHeader {");
+      outFile.println("   left:0; right: 0;");
+      outFile.println("   top:0; height: 6em;");
+      outFile.println("  text-align: center;");
+      outFile.println("background-color: #728FCE;");
+      outFile.println("}");
+
+      outFile.println(".mainContent {");
+      outFile.println("  left:0; right: 0;");
+      outFile.println("  top: 6em; bottom: 0;");
+      outFile.println("}");
+
+      outFile.println(".header {");
+      outFile.println("  left:0; right: 0;");
+      outFile.println(" top:0; height: 2em;");
+      outFile.println("  padding: 0.5em;");
+      outFile.println("}");
+
+      outFile.println(".innerContent {");
+      outFile.println("   left:0; right: 0;");
+      outFile.println("  top: 2em; bottom: 0;");
+      outFile.println("}");
+
+      outFile.println(".frame {");
+      outFile.println(" width: 100%; height: 100%;");
+      outFile.println("  border: 0px;");
+      outFile.println("}");
+
+      outFile.println(".packages {");
+      outFile.println(" left:0; right: 0;");
+      outFile.println(" top:0; bottom: 50%;");
+      outFile.println("}");
+
+      outFile.println(".codeType {");
+      outFile.println("left:0; right: 0;");
+      outFile.println("top:50%; bottom: 0;");
+      outFile.println("}");
+      outFile.println("</style>");
+      outFile.println("</head>");
+
+      outFile.println("<body>");
+      outFile.println("<div class='abs mainHeader'>");
+      outFile.println("<h2>Classes in package \"" + packageName + "\"</h2>");
+      addHeaderWithBreakdownContext(breakdown, outFile);
+      outFile.println("</center>");
+      outFile.println("<hr>");
+      outFile.println("</div>");
+      outFile.println("<div class='abs mainContent' style='overflow:auto'>");
+      outFile.println("<table style='width:100%'>");
+      outFile.println("<thead>");
+      outFile.println("<th class='barlabel'>Size</th>");
+      outFile.println("<th class='barlabel'>Percentage</th>");
+      outFile.println("<th class='barlabel'></th>");
+      outFile.println("<th style='width:100%' class='barlabel'></th>");
+      outFile.println("</thead>");
+//
+      for (Float size : sortedClasses.keySet()) {
+
+        String className = sortedClasses.get(size);
+        float ratio = (size / maxSize) * 85;
+        if (ratio < 5) {
+          ratio = 5;
+        }
+        float perc = (size / sumSize) * 100;
+
+        String dependencyLink = depLinker.dependencyLinkForClass(className,
+            permutationId);
+        outFile.println("<tr>");
+        outFile.println("<td class=\"barlabel\">" + size + "</td>");
+        outFile.println("<td class=\"barlabel\">" + perc + "%</td>");
+        if (dependencyLink != null) {
+          outFile.println("<td class=\"barlabel\"><a href=\"" + dependencyLink
+              + "\" target=\"_top\">" + className + "</a></td>");
+        } else {
+          outFile.println("<td class=\"barlabel\">" + className + "</td>");
+        }
+        outFile.println("<td class=\"box\">");
+        outFile.println("  <div style=\"width:"
+            + ratio
+            + "%;\" class=\"lb\"><div class=\"rb\"><div class=\"bb\"><div class=\"blc\"><div class=\"brc\"><div class=\"tb\"><div class=\"tlc\"><div class=\"trc\"><div class=\"content\"></div></div></div></div></div></div></div></div>");
+        outFile.println("</td>");
+        outFile.println("</tr>");
+      }
+      outFile.println("</table>");
+      outFile.println("</div>");
+      addStandardHtmlEnding(outFile);
+      outFile.close();
+    }
+  }
+
+  public void makeSplitStatusPages(String permutationId) throws IOException {
+    for (String className : globalInformation.getClassToPackage().keySet()) {
+      makeSplitStatusPage(className, permutationId);
+    }
+  }
+
+  public void makeTopLevelHtmlForAllPerms() throws FileNotFoundException {
+
+    PrintWriter outFile = new PrintWriter(getOutFile("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 all Permutations</title>");
+    outFile.println("<style type=\"text/css\">");
+    outFile.println("body {background-color: #728FCE}");
+    outFile.println("h2 {background-color: transparent}");
+    outFile.println("</style>");
+    outFile.println("</head>");
+
+    outFile.println("<body>");
+    outFile.println("<center>");
+    outFile.println("<h1>Story of Your Compile</h1>");
+    outFile.println("<hr>");
+    outFile.println("<h3>Story of Your Compile - Overview of Permutations</h3>");
+    outFile.println("<hr>");
+
+    outFile.println("<div style='overflow:auto; background-color:white'>");
+    outFile.println("<center>");
+    for (String permutationId : settings.allPermsInfo.keySet()) {
+      String permutationInfo = settings.allPermsInfo.get(permutationId);
+      outFile.print("<p><a href=\"SoycDashboard" + "-" + permutationId
+          + "-index.html\">Permutation " + permutationId);
+      if (permutationInfo.length() > 0) {
+        outFile.println(" (" + permutationInfo + ")" + "</a>");
+      } else {
+        outFile.println("</a>");
+      }
+    }
+    outFile.println("</center>");
+    outFile.println("</div>");
+    addStandardHtmlEnding(outFile);
+    outFile.close();
+  }
+
+  public void makeTopLevelShell(String permutationId) throws IOException {
+    PrintWriter outFile = new PrintWriter(getOutFile("SoycDashboard" + "-"
+        + permutationId + "-index.html"));
+
+    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("<title>Story of Your Compile - Top Level Dashboard for Permutation</title>");
+    outFile.println("  <meta http-equiv=\"content-type\" content=\"text/html;charset=ISO-8859-1\">");
+    outFile.println("  <link rel=\"stylesheet\" href=\"common.css\" media=\"screen\">");
+
+    outFile.println("  <style type=\"text/css\">");
+
+    outFile.println(".abs {");
+    outFile.println("  position: absolute;");
+    outFile.println(" overflow: hidden;");
+    outFile.println(" }");
+
+    outFile.println(" .mainHeader {");
+    outFile.println("   left:0; right: 0;");
+    outFile.println("   top:0; height: 6em;");
+    outFile.println("  text-align: center;");
+    outFile.println("background-color: #728FCE;");
+    outFile.println("}");
+
+    outFile.println(".mainContent {");
+    outFile.println("  left:0; right: 0;");
+    outFile.println("  top: 6em; bottom: 0;");
+    outFile.println("}");
+
+    outFile.println(".header {");
+    outFile.println("  left:0; right: 0;");
+    outFile.println(" top:0; height: 2em;");
+    outFile.println("  padding: 0.5em;");
+    outFile.println("}");
+
+    outFile.println(".innerContent {");
+    outFile.println("   left:0; right: 0;");
+    outFile.println("  top: 2em; bottom: 0;");
+    outFile.println("}");
+
+    outFile.println(".frame {");
+    outFile.println(" width: 100%; height: 100%;");
+    outFile.println("  border: 0px;");
+    outFile.println("}");
+
+    outFile.println(".packages {");
+    outFile.println(" left:0; right: 0;");
+    outFile.println(" top:0; bottom: 50%;");
+    outFile.println("}");
+
+    outFile.println(".codeType {");
+    outFile.println("left:0; right: 0;");
+    outFile.println("top:50%; bottom: 0;");
+    outFile.println("}");
+    outFile.println("</style>");
+    outFile.println("</head>");
+
+    outFile.println("<body>");
+    outFile.println("<div class='abs mainHeader'>");
+    outFile.println("<h2>Story of Your Compile Dashboard</h2>");
+
+    outFile.println("<hr>");
+    outFile.println("<center>");
+    if (globalInformation.getSplitPointToLocation().size() > 1) {
+      outFile.println("<b>Initial download size: <span style=\"color:maroon\">"
+          + globalInformation.getInitialCodeBreakdown().sizeAllCode
+          + "</span></span></b>");
+    }
+    outFile.println("<b>Full code size: <span style=\"color:maroon\">"
+        + globalInformation.getTotalCodeBreakdown().sizeAllCode
+        + "</span></span></b>");
+
+    outFile.println("<hr>");
+    outFile.println("Available code subsets to analyze");
+    outFile.println("<hr>");
+    outFile.println("</div>");
+
+    outFile.println("<div class='abs mainContent' style=\"overflow: auto\" >");
+    outFile.println("<table style='width:100%'>");
+    outFile.println("<thead>");
+    outFile.println("<th class='barlabel'>Size</th>");
+    outFile.println("<th class='barlabel'>Percentage</th>");
+    outFile.println("<th class='barlabel'></th>");
+    outFile.println("<th style='width:100%' class='barlabel'></th>");
+    outFile.println("</thead>");
+
+    int numSplitPoints = globalInformation.getSplitPointToLocation().size();
+
+    int numRows = 2;
+    if (numSplitPoints > 0) {
+      // add one for the leftovers fragment
+      numRows += numSplitPoints + 1;
+    }
+    int outerHeight = 25 * numRows;
+    outFile.println("<div style=\"width:100%; margin:20px 0 20px 0; background-color:white;position:relative;height:"
+        + outerHeight + "\">");
+    float maxSize = globalInformation.getTotalCodeBreakdown().sizeAllCode;
+
+    for (int i = FRAGMENT_NUMBER_TOTAL_PROGRAM; i <= numSplitPoints + 1; i++) {
+      if (i == 1 && numSplitPoints == 0) {
+        // don't show the leftovers fragment if the split points aren't known
+        continue;
+      }
+
+      SizeBreakdown breakdown;
+      if (i == FRAGMENT_NUMBER_TOTAL_PROGRAM) {
+        breakdown = globalInformation.getTotalCodeBreakdown();
+      } else if (i == numSplitPoints + 1) {
+        breakdown = globalInformation.getLeftoversBreakdown();
+      } else if (i == FRAGMENT_NUMBER_INITIAL_DOWNLOAD) {
+        breakdown = globalInformation.getInitialCodeBreakdown();
+      } else {
+        breakdown = globalInformation.splitPointCodeBreakdown(i);
+      }
+
+      String drillDownFileName = shellFileName(breakdown, permutationId);
+      String splitPointDescription = breakdown.getDescription();
+
+      int size = breakdown.sizeAllCode;
+      float ratio;
+      if (globalInformation.getInitialCodeBreakdown().sizeAllCode > 0) {
+        ratio = (size / globalInformation.getInitialCodeBreakdown().sizeAllCode) * 79;
+      } else {
+        ratio = (size / maxSize) * 79;
+      }
+      if (ratio < 5) {
+        ratio = 5;
+      }
+      float perc = (size / maxSize) * 100;
+
+      outFile.println("<tr>");
+      outFile.println("<td class=\"barlabel\">" + size + "</td>");
+      outFile.println("<td class=\"barlabel\">" + perc + "%</td>");
+      outFile.println("<td class=\"barlabel\"><a href=\"" + drillDownFileName
+          + "\" target=\"_top\">" + splitPointDescription + "</a></td>");
+      outFile.println("<td class=\"box\">");
+      if (splitPointDescription.compareTo("Total program") != 0) {
+        outFile.println("<div style=\"width:"
+            + ratio
+            + "%;\" class=\"lb\"><div class=\"rb\"><div class=\"bb\"><div class=\"blc\"><div class=\"brc\"><div class=\"tb\"><div class=\"tlc\"><div class=\"trc\"><div class=\"content\"></div></div></div></div></div></div></div></div>");
+      }
+      outFile.println("</td>");
+      outFile.println("</tr>");
+    }
+    outFile.println("</div>");
+    outFile.println("</body></html>");
+    outFile.close();
+  }
+
+  public void setGlobalInformation(GlobalInformation globalInformation) {
+    this.globalInformation = globalInformation;
+  }
+
+  public void setSettings(Settings settings) {
+    this.settings = settings;
+  }
+
+  private void addDependenciesHtmlProlog(final PrintWriter out, String title,
+      String header) {
+    out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
+    out.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
+    out.println("<html>");
+
+    out.println("<head>");
+    out.println("<meta http-equiv=\"content-type\" content=\"text/html;charset=ISO-8859-1\">");
+    out.println("<title>" + title + "</title>");
+    out.println("<link rel=\"stylesheet\" href=\"common.css\">");
+    out.println("<style type=\"text/css\">");
+    out.println("body {background-color: #728FCE}");
+    out.println("h2 {background-color: transparent}");
+    out.println("p {background-color: fuchsia}");
+    out.println(".calledBy {");
+    out.println("  color: green;");
+    out.println("}");
+    out.println(".toggle {");
+    out.println("cursor: pointer;");
+    out.println("}");
+    out.println(".main {");
+    out.println("background-color: white;");
+    out.println("padding: 8px;");
+    out.println("}");
+    out.println("</style>");
+    out.println("<script>");
+    out.println("function nextSiblingElement(a) {");
+    out.println("var ul = a.nextSibling;");
+    out.println("while (ul && ul.nodeType != 1) { // 1==element");
+    out.println("  ul = ul.nextSibling;");
+    out.println("}");
+    out.println("return ul;");
+    out.println("}");
+    out.println("function toggle() {");
+    out.println("var ul = nextSiblingElement(this);");
+    out.println("if (ul) {");
+    out.println(" ul.style.display = (ul.style.display == 'none') ? '' : 'none';");
+    out.println("}");
+    out.println("}");
+    out.println("</script>");
+
+    out.println("</head>");
+
+    out.println("<body>");
+    out.println("<center>");
+    out.println("<h2>" + title + "</h2>");
+    if (header != null) {
+      addCenteredHeader(out, header);
+    }
+    out.println("</center>");
+  }
+
+  private void addLefttoversStatus(String className, String packageName,
+      PrintWriter out, String permutationId) {
+    out.println("<tr><td>&nbsp;&nbsp;&nbsp;&nbsp;<a href=\""
+        + dependenciesFileName("total", packageName, permutationId) + "#"
+        + className + "\">See why it's live</a></td></tr>");
+    for (int sp = 1; sp <= globalInformation.getNumSplitPoints(); sp++) {
+      out.println("<tr><td>&nbsp;&nbsp;&nbsp;&nbsp;<a href=\""
+          + dependenciesFileName("sp" + sp, packageName, permutationId) + "#"
+          + className + "\">See why it's not exclusive to s.p. #" + sp + " ("
+          + globalInformation.getSplitPointToLocation().get(sp)
+          + ")</a></td></tr>");
+    }
+  }
+
+  private void addStandardHtmlEnding(final PrintWriter out) {
+    out.println("</div>");
+    out.println("</body>");
+    out.println("</html>");
+  }
+
+  private void addStandardHtmlProlog(final PrintWriter out, String title,
+      String header) {
+    out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"");
+    out.println("\"http://www.w3.org/TR/html4/strict.dtd\">");
+    out.println("<html>");
+    out.println("<head>");
+    out.println("<meta http-equiv=\"content-type\" content=\"text/html;charset=ISO-8859-1\">");
+    out.println("<title>" + title + "</title>");
+    out.println("</head>");
+
+    out.println("<style type=\"text/css\">");
+    out.println("body {background-color: #728FCE}");
+    out.println("h2 {background-color: transparent}");
+    out.println("p {background-color: fuchsia}");
+    out.println("</style>");
+
+    out.println("<body>");
+    out.println("<center>");
+    out.println("<h2>" + title + "</h2>");
+    if (header != null) {
+      addCenteredHeader(out, header);
+    }
+    out.println("</center>");
+  }
+
+  private String dependenciesFileName(String depGraphName, String packageName,
+      String permutationId) {
+    return "methodDependencies-" + depGraphName + "-" + filename(packageName)
+        + "-" + permutationId + ".html";
+  }
+
+  /**
+   * Return a {@link File} object for a file to be emitted into the output
+   * directory.
+   */
+  private File getOutFile(String localFileName) {
+    File outDir = new File(settings.out.get());
+    return new File(outDir, localFileName);
+  }
+
+  /**
+   * Describe the code covered by the dependency graph with the supplied name.
+   */
+  private String inferDepGraphDescription(String depGraphName) {
+    if (depGraphName.equals("initial")) {
+      return "Initially Live Code";
+    }
+
+    if (depGraphName.equals("total")) {
+      return "All Code";
+    }
+
+    Matcher matcher = PATTERN_SP_INT.matcher(depGraphName);
+    if (matcher.matches()) {
+      int splitPoint = Integer.valueOf(matcher.group(1));
+      if (isInitialSplitPoint(splitPoint)) {
+        return "Code Becoming Live at Split Point " + splitPoint;
+      } else {
+        return "Code not Exclusive to Split Point " + splitPoint;
+      }
+    }
+
+    throw new RuntimeException("Unexpected dependency graph name: "
+        + depGraphName);
+  }
+
+  private boolean isInitialSplitPoint(int splitPoint) {
+    return globalInformation.getSplitPointInitialLoadSequence().contains(
+        splitPoint);
+  }
+
+  private String leftoversStatusFileName(String className, String permutationId) {
+    return "leftoverStatus-" + filename(className) + "-" + permutationId
+        + ".html";
+  }
+
+  private String makeCodeTypeHtml(SizeBreakdown breakdown,
+      Map<String, CodeCollection> nameToCodeColl,
+      Map<String, LiteralsCollection> nameToLitColl, String permutationId)
+      throws IOException {
+    String outFileName = breakdown.getId() + "-" + permutationId
+        + "_codeTypeBreakdown.html";
+    float maxSize = 0f;
+    float sumSize = 0f;
+    TreeMap<Float, String> sortedCodeTypes = new TreeMap<Float, String>(
+        Collections.reverseOrder());
+
+    for (String codeType : nameToCodeColl.keySet()) {
+      float curSize = nameToCodeColl.get(codeType).getCumPartialSize(breakdown);
+      sumSize += curSize;
+
+      if (curSize != 0f) {
+        sortedCodeTypes.put(curSize, codeType);
+        if (curSize > maxSize) {
+          maxSize = curSize;
+        }
+      }
+    }
+
+    final PrintWriter outFile = new PrintWriter(getOutFile(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("<link rel=\"stylesheet\" href=\"common.css\" media=\"screen\">");
+    outFile.println("</head>");
+
+    outFile.println("<body>");
+    outFile.println("<table style='width:100%'>");
+    outFile.println("<thead>");
+    outFile.println("<th class='barlabel'>Size</th>");
+
+    outFile.println("<th class='barlabel'>Percentage</th>");
+    outFile.println("<th class='barlabel'></th>");
+    outFile.println("<th style='width:100%' class='barlabel'></th>");
+    outFile.println("</thead>");
+
+    for (Float size : sortedCodeTypes.keySet()) {
+
+      String codeType = sortedCodeTypes.get(size);
+      String drillDownFileName = breakdown.getId() + "_" + codeType + "-"
+          + permutationId + "Classes.html";
+
+      float ratio = (size / maxSize) * 79;
+      float perc = (size / sumSize) * 100;
+
+      if (ratio < 5) {
+        ratio = 5;
+      }
+
+      outFile.println("<tr>");
+      outFile.println("<td class=\"barlabel\">" + size + "</td>");
+      outFile.println("<td class=\"barlabel\">" + perc + "%</td>");
+      outFile.println("<td class=\"barlabel\"><a href=\"" + drillDownFileName
+          + "\" target=\"_top\">" + codeType + "</a></td>");
+      outFile.println("<td class=\"box\">");
+      outFile.println("<div style=\"width:"
+          + ratio
+          + "%;\" class=\"lb\"><div class=\"rb\"><div class=\"bb\"><div class=\"blc\"><div class=\"brc\"><div class=\"tb\"><div class=\"tlc\"><div class=\"trc\"><div class=\"content\"></div></div></div></div></div></div></div></div>");
+      outFile.println("</td>");
+      outFile.println("</tr>");
+    }
+
+    maxSize = 0f;
+    sumSize = 0f;
+    TreeMap<Float, String> sortedLitTypes = new TreeMap<Float, String>(
+        Collections.reverseOrder());
+
+    for (String literal : nameToLitColl.keySet()) {
+      float curSize = nameToLitColl.get(literal).size;
+      sumSize += curSize;
+
+      if (curSize != 0f) {
+        sortedLitTypes.put(curSize, literal);
+
+        if (curSize > maxSize) {
+          maxSize = curSize;
+        }
+      }
+    }
+
+    for (Float size : sortedLitTypes.keySet()) {
+      String literal = sortedLitTypes.get(size);
+      String drillDownFileName = breakdown.getId() + "_" + literal + "-"
+          + permutationId + "Lits.html";
+
+      float ratio = (size / maxSize) * 79;
+      float perc = (size / sumSize) * 100;
+
+      if (ratio < 5) {
+        ratio = 5;
+      }
+
+      outFile.println("<tr>");
+      outFile.println("<td class=\"barlabel\">" + size + "</td>");
+      outFile.println("<td class=\"barlabel\">" + perc + "%</td>");
+      outFile.println("<td class=\"barlabel\"><a href=\"" + drillDownFileName
+          + "\" target=\"_top\">" + literal + "</a></td>");
+      outFile.println("<td class=\"box\">");
+      outFile.println("<div style=\"width:"
+          + ratio
+          + "%;\" class=\"lb\"><div class=\"rb\"><div class=\"bb\"><div class=\"blc\"><div class=\"brc\"><div class=\"tb\"><div class=\"tlc\"><div class=\"trc\"><div class=\"content\"></div></div></div></div></div></div></div></div>");
+      outFile.println("</td>");
+      outFile.println("</tr>");
+    }
+
+    outFile.println("</table>");
+    outFile.println("</body>");
+    outFile.println("</html>");
+    outFile.close();
+
+    return outFileName;
+  }
+
+  private void makeDependenciesHtml(String depGraphName,
+      Map<String, String> dependencies, String permutationId)
+      throws FileNotFoundException {
+    String depGraphDescription = inferDepGraphDescription(depGraphName);
+    PrintWriter outFile = null;
+    String curPackageName = "";
+    String curClassName = "";
+
+    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}.*", "");
+
+      String className = method;
+      className = className.replaceAll("::.*", "");
+
+      if ((curPackageName.compareTo("") == 0)
+          || (curPackageName.compareTo(packageName) != 0)) {
+
+        curPackageName = packageName;
+        if (outFile != null) {
+          // finish up the current file
+          addStandardHtmlEnding(outFile);
+          outFile.close();
+        }
+
+        String outFileName = dependenciesFileName(depGraphName, curPackageName,
+            permutationId);
+        outFile = new PrintWriter(getOutFile(outFileName));
+
+        String packageDescription = packageName.length() == 0
+            ? "the default package" : packageName;
+        addDependenciesHtmlProlog(outFile, "Method Dependencies for "
+            + depGraphDescription, "Showing Package: " + packageDescription);
+      }
+      String name = method;
+      if (curClassName.compareTo(className) != 0) {
+        name = className;
+        curClassName = className;
+        outFile.println("<h3>Class: " + curClassName + "</h3>");
+      }
+
+      outFile.println("<div class='main'>");
+      outFile.println("<a class='toggle' onclick='toggle.call(this)' name="
+          + name + "><span class='calledBy'> Call stack: </span>" + name
+          + "</a>");
+      outFile.println("<ul>");
+
+      String depMethod = dependencies.get(method);
+      while (depMethod != null) {
+        String nextDep = dependencies.get(depMethod);
+        if (nextDep != null) {
+          outFile.println("<li>" + depMethod + "</li>");
+        }
+        depMethod = nextDep;
+      }
+      outFile.println("</ul>");
+      outFile.println("</div>");
+    }
+    addStandardHtmlEnding(outFile);
+    outFile.close();
+  }
+
+  private void makeLeftoversStatusPage(String className, String permutationId)
+      throws IOException {
+    String packageName = globalInformation.getClassToPackage().get(className);
+    PrintWriter out = new PrintWriter(getOutFile(leftoversStatusFileName(
+        className, permutationId)));
+
+    addStandardHtmlProlog(out, "Leftovers page for " + className, null);
+
+    out.println("<center>");
+    out.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
+
+    out.println("<tr><td>This class has some leftover code, neither initial nor exclusive to any split point:</td></tr>");
+    addLefttoversStatus(className, packageName, out, permutationId);
+    out.println("</table>");
+
+    addStandardHtmlEnding(out);
+
+    out.close();
+  }
+
+  private String makePackageHtml(SizeBreakdown breakdown, String permutationId)
+      throws FileNotFoundException {
+    String outFileName = breakdown.getId() + "-" + permutationId + "_"
+        + "packageBreakdown.html";
+    Map<String, Integer> packageToPartialSize = breakdown.packageToSize;
+    TreeMap<Integer, String> sortedPackages = new TreeMap<Integer, String>(
+        Collections.reverseOrder());
+    float maxSize = 0f;
+    float sumSize = 0f;
+    for (String packageName : packageToPartialSize.keySet()) {
+      sortedPackages.put(packageToPartialSize.get(packageName), packageName);
+      sumSize += packageToPartialSize.get(packageName);
+      if (packageToPartialSize.get(packageName) > maxSize) {
+        maxSize = packageToPartialSize.get(packageName);
+      }
+    }
+
+    final PrintWriter outFile = new PrintWriter(getOutFile(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("<link rel=\"stylesheet\" href=\"common.css\" media=\"screen\">");
+    outFile.println("</head>");
+    outFile.println("<body>");
+    outFile.println("<table style='width:100%'>");
+    outFile.println("<thead>");
+    outFile.println("<th class='barlabel'>Size</th>");
+
+    outFile.println("<th class='barlabel'>Percentage</th>");
+    outFile.println("<th class='barlabel'>Package</th>");
+    outFile.println("<th style='width:100%' class='barlabel'></th>");
+    outFile.println("</thead>");
+
+    for (int size : sortedPackages.keySet()) {
+      String packageName = sortedPackages.get(size);
+      String drillDownFileName = classesInPackageFileName(breakdown,
+          packageName, permutationId);
+
+      float ratio = (size / maxSize) * 79;
+      if (ratio < 5) {
+        ratio = 5;
+      }
+      float perc = (size / sumSize) * 100;
+
+      outFile.println("<tr>");
+      outFile.println("<td class='barlabel'>" + size + "</td>");
+      outFile.println("<td class='barlabel'>" + perc + "</td>");
+      outFile.println("<td class='barlabel'><a href=\"" + drillDownFileName
+          + "\" target=\"_top\">" + packageName + "</a></td>");
+      outFile.println("<td class=\"box\">");
+      outFile.println("<div style=\"width:"
+          + ratio
+          + "%;\" class=\"lb\"><div class=\"rb\"><div class=\"bb\"><div class=\"blc\"><div class=\"brc\"><div class=\"tb\"><div class=\"tlc\"><div class=\"trc\"><div class=\"content\"></div></div></div></div></div></div></div></div>");
+      outFile.println("</td>");
+      outFile.println("</tr>");
+    }
+
+    outFile.println("</table>");
+    outFile.println("</body>");
+    outFile.println("</html>");
+    outFile.close();
+
+    return outFileName;
+  }
+
+  private void makeSplitStatusPage(String className, String permutationId)
+      throws IOException {
+    String packageName = globalInformation.getClassToPackage().get(className);
+    PrintWriter out = new PrintWriter(getOutFile(splitStatusFileName(className,
+        permutationId)));
+
+    addStandardHtmlProlog(out, "Split point status for " + className, null);
+
+    out.println("<center>");
+    out.println("<table border=\"1\" width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
+
+    if (globalInformation.getInitialCodeBreakdown().classToSize.containsKey(className)) {
+      out.println("<tr><td>Some code is initial (<a href=\""
+          + dependenciesFileName("initial", packageName, permutationId) + "#"
+          + className + "\">see why</a>)</td></tr>");
+    }
+    for (int sp : splitPointsWithClass(className)) {
+      out.println("<tr><td>Some code downloads with s.p. #" + sp + " ("
+          + globalInformation.getSplitPointToLocation().get(sp) + ")</td></tr>");
+    }
+    if (globalInformation.getLeftoversBreakdown().classToSize.containsKey(className)) {
+      out.println("<tr><td>Some code is left over:</td></tr>");
+      addLefttoversStatus(className, packageName, out, permutationId);
+    }
+    out.println("</table>");
+
+    addStandardHtmlEnding(out);
+
+    out.close();
+  }
+
+  /**
+   * Find which split points include code belonging to <code>className</code>.
+   */
+  private Iterable<Integer> splitPointsWithClass(String className) {
+    List<Integer> sps = new ArrayList<Integer>();
+    for (int sp = 1; sp <= globalInformation.getNumSplitPoints(); sp++) {
+      Map<String, Integer> classToSize = globalInformation.splitPointCodeBreakdown(sp).classToSize;
+      if (classToSize.containsKey(className)) {
+        sps.add(sp);
+      }
+    }
+    return sps;
+  }
+
+  private String splitStatusFileName(String className, String permutationId) {
+    return "splitStatus-" + filename(className) + "-" + permutationId + ".html";
+  }
+}
diff --git a/dev/core/src/com/google/gwt/soyc/Settings.java b/dev/core/src/com/google/gwt/soyc/Settings.java
new file mode 100644
index 0000000..8b9732d
--- /dev/null
+++ b/dev/core/src/com/google/gwt/soyc/Settings.java
@@ -0,0 +1,257 @@
+/*
+ * 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 java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.TreeMap;
+
+/**
+ * Command-line settings for SOYC.
+ */
+public class Settings {
+  /**
+   * An exception indicating that there is a problem in an argument list.
+   */
+  public static class ArgumentListException extends Exception {
+    public ArgumentListException(String message) {
+      super(message);
+    }
+  }
+
+  /**
+   * One individual setting.
+   */
+  public abstract static class Setting<T> {
+    private final String help;
+    private T value;
+
+    public Setting(T initialValue, String help) {
+      value = initialValue;
+      this.help = help;
+    }
+
+    public T get() {
+      return value;
+    }
+
+    public String getHelp() {
+      return help;
+    }
+
+    public void set(T newValue) {
+      value = newValue;
+    }
+
+    /**
+     * Consume arguments from the front of the list. If the front of the
+     * argument list is not a match, do nothing. If the front of the argument
+     * list is a match but has some problem, then throw an exception.
+     */
+    abstract boolean consumeArguments(List<String> arguments)
+        throws ArgumentListException;
+  }
+
+  /**
+   * A setting that is an option followed by a string argument.
+   */
+  public static class StringSetting extends Setting<String> {
+    private final String option;
+
+    public StringSetting(String option, String argumentName,
+        String defaultSetting, String description) {
+      super(defaultSetting, option + " " + argumentName + "    " + description);
+      this.option = option;
+    }
+
+    @Override
+    public String toString() {
+      return option + " " + get();
+    }
+
+    @Override
+    boolean consumeArguments(List<String> arguments)
+        throws ArgumentListException {
+      if (arguments.get(0).equals(option)) {
+        if (arguments.size() < 2) {
+          throw new ArgumentListException("Option " + option
+              + " requires an argument");
+        }
+        arguments.remove(0);
+        set(arguments.remove(0));
+        return true;
+      }
+
+      return false;
+    }
+  }
+
+  public static Settings fromArgumentList(String[] allArguments)
+      throws ArgumentListException {
+    Settings settings = new Settings();
+
+    List<String> remainingArguments = new LinkedList<String>(
+        Arrays.asList(allArguments));
+
+    // Handle hyphenated options
+    next_argument : while (!remainingArguments.isEmpty()) {
+      for (Setting<?> setting : settings.allSettings) {
+        if (setting.consumeArguments(remainingArguments)) {
+          continue next_argument;
+        }
+      }
+      break; // No setting wanted the remaining arguments
+    }
+
+    if (settings.resources.get() == null) {
+      throw new ArgumentListException("The -resources option is required");
+    }
+
+    if ((settings.soycDir.get() == null)
+        && (settings.symbolMapsDir.get() == null)) {
+
+      // If in legacy command line mode, handle bare arguments at the end of the
+      // list
+      if (remainingArguments.isEmpty()) {
+        throw new ArgumentListException("Must specify the soyc directory");
+      }
+
+      if (remainingArguments.get(0).startsWith("-")) {
+        throw new ArgumentListException("Unrecognized argument: "
+            + remainingArguments.get(0));
+      }
+
+      settings.storiesFileName = remainingArguments.remove(0);
+
+      if (!remainingArguments.isEmpty()) {
+        settings.depFileName = remainingArguments.remove(0);
+      }
+      if (!remainingArguments.isEmpty()) {
+        settings.splitPointsFileName = remainingArguments.remove(0);
+      }
+
+      if (!remainingArguments.isEmpty()) {
+        throw new ArgumentListException("Too many arguments");
+      }
+
+    } else if (settings.soycDir.get() != null) {
+      if (settings.symbolMapsDir.get() == null) {
+        throw new ArgumentListException(
+            "Must specify symbol maps directory when specifying Soyc directory.");
+      }
+    } else {
+      if (settings.soycDir.get() == null) {
+        throw new ArgumentListException(
+            "Must specify Soyc directory when specifying symbol maps directory .");
+      }
+    }
+
+    // if the output directory doesn't exist, create it before going on
+    if (settings.out.get() != null) {
+      File dir = new File(settings.out.get());
+      if (!dir.exists()) {
+        dir.mkdir();
+      }
+    }
+    return settings;
+  }
+
+  public static String settingsHelp() {
+    StringBuffer help = new StringBuffer();
+    for (Setting<?> setting : new Settings().allSettings) {
+      help.append(setting.getHelp() + "\n");
+    }
+    return help.toString();
+  }
+
+  public Map<String, String> allPermsInfo = new TreeMap<String, String>();
+  public String depFileName;
+  public Boolean displayDependencies = false;
+  public Boolean displaySplitPoints = false;
+
+  public final Setting<String> out = addSetting(new StringSetting("-out",
+      "dir", ".", "output directory"));
+
+  public final Setting<String> resources = addSetting(new StringSetting(
+      "-resources", "jarfile", null,
+      " directory or jar file with CSS, etc., resources"));
+
+  public final Setting<String> soycDir = addSetting(new StringSetting(
+      "-soycDir", "dir", null, " directory for soyc files"));
+
+  public String splitPointsFileName;
+
+  public String storiesFileName;
+
+  public final Setting<String> symbolMapsDir = addSetting(new StringSetting(
+      "-symbolMapsDir", "dir", null, " directory or symbol maps files"));
+
+  private List<Setting<?>> allSettings;
+  public void readPermutationInfo() throws FileNotFoundException {
+
+    if (symbolMapsDir.get() == null) {
+      // get the permutation id from settings
+      String permutationId = storiesFileName;
+      permutationId = permutationId.replaceAll(".*/stories", "");
+      permutationId = permutationId.replaceAll("\\.xml(\\.gz)?", "");
+      allPermsInfo.put(permutationId, "");
+    } else {
+      File dir = new File(symbolMapsDir.get());
+      String files[] = dir.list();
+      for (Integer i = 0; i < files.length; i++) {
+        String permFileName = symbolMapsDir.get() + "/" + files[i];
+        FileReader fir = new FileReader(permFileName);
+
+        Scanner sc = new Scanner(fir);
+
+        String permutationId = "";
+        String permutationInfo = "";
+        int lineCount = 0;
+        while ((sc.hasNextLine()) && (lineCount < 2)) {
+
+          String curLine = sc.nextLine();
+          curLine = curLine.replace("# {", "");
+          curLine = curLine.replace("}", "");
+          curLine = curLine.trim();
+
+          if (lineCount == 0) {
+            permutationId = curLine;
+          } else {
+            permutationInfo = curLine;
+          }
+          lineCount++;
+        }
+        allPermsInfo.put(permutationId, permutationInfo);
+      }
+    }
+  }
+
+  private <T> Setting<T> addSetting(Setting<T> setting) {
+    if (allSettings == null) {
+      allSettings = new ArrayList<Setting<?>>();
+    }
+    allSettings.add(setting);
+    return setting;
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/soyc/SizeBreakdown.java b/dev/core/src/com/google/gwt/soyc/SizeBreakdown.java
new file mode 100644
index 0000000..672b23b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/soyc/SizeBreakdown.java
@@ -0,0 +1,88 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A size breakdown of one code collection.
+ */
+public class SizeBreakdown {
+  public Map<String, Integer> classToSize = new HashMap<String, Integer>();
+
+  public HashMap<String, CodeCollection> nameToCodeColl = new HashMap<String, CodeCollection>();
+
+  public Map<String, LiteralsCollection> nameToLitColl = new TreeMap<String, LiteralsCollection>();
+  public Map<String, Integer> packageToSize = new HashMap<String, Integer>();
+  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;
+  }
+
+  @Override
+  public String toString() {
+    return getId();
+  }
+
+  private void initializeLiteralsCollection(
+      Map<String, LiteralsCollection> nameToLitColl) {
+    nameToLitColl.put("long", new LiteralsCollection());
+    nameToLitColl.put("null", new LiteralsCollection());
+    nameToLitColl.put("class", new LiteralsCollection());
+    nameToLitColl.put("int", new LiteralsCollection());
+    nameToLitColl.put("string", new LiteralsCollection());
+    nameToLitColl.put("number", new LiteralsCollection());
+    nameToLitColl.put("boolean", new LiteralsCollection());
+    nameToLitColl.put("double", new LiteralsCollection());
+    nameToLitColl.put("char", new LiteralsCollection());
+    nameToLitColl.put("undefined", new LiteralsCollection());
+    nameToLitColl.put("float", new LiteralsCollection());
+  }
+
+  private void initializeNameToCodeCollection(
+      HashMap<String, CodeCollection> nameToCodeColl) {
+    nameToCodeColl.put("allOther", new CodeCollection());
+    nameToCodeColl.put("widget", new CodeCollection());
+    nameToCodeColl.put("rpcUser", new CodeCollection());
+    nameToCodeColl.put("rpcGen", new CodeCollection());
+    nameToCodeColl.put("rpcGwt", new CodeCollection());
+    nameToCodeColl.put("gwtLang", new CodeCollection());
+    nameToCodeColl.put("jre", new CodeCollection());
+  }
+}
diff --git a/dev/core/src/com/google/gwt/soyc/SoycDashboard.java b/dev/core/src/com/google/gwt/soyc/SoycDashboard.java
new file mode 100644
index 0000000..25aeffd
--- /dev/null
+++ b/dev/core/src/com/google/gwt/soyc/SoycDashboard.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2008 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.soyc.MakeTopLevelHtmlForPerm.DependencyLinker;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Map.Entry;
+import java.util.zip.GZIPInputStream;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * The command-line entry point for creating a SOYC report.
+ */
+public class SoycDashboard {
+
+  private static class FormatException extends RuntimeException {
+    public FormatException() {
+      super();
+    }
+
+    public FormatException(String message) {
+      super(message);
+    }
+
+    public FormatException(Throwable cause) {
+      super(cause);
+    }
+  }
+
+  public static void main(final String[] args) {
+    try {
+      System.out.println("Generating the Story of Your Compile...");
+      Settings settings = Settings.fromArgumentList(args);
+
+      MakeTopLevelHtmlForPerm makeTopLevelHtml = new MakeTopLevelHtmlForPerm();
+      makeTopLevelHtml.setSettings(settings);
+
+      // read in all the symbol maps
+      settings.readPermutationInfo();
+      makeTopLevelHtml.makeTopLevelHtmlForAllPerms();
+      for (String permutationId : settings.allPermsInfo.keySet()) {
+        GlobalInformation globalInformation = new GlobalInformation();
+        MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm = new MakeTopLevelHtmlForPerm(
+            globalInformation);
+        makeTopLevelHtmlForPerm.setSettings(settings);
+
+        String storiesFileName = settings.storiesFileName;
+
+        String depFileName = settings.depFileName;
+        if (depFileName == null) {
+          depFileName = "";
+        }
+        String splitPointsFileName = settings.splitPointsFileName;
+        if (splitPointsFileName == null) {
+          splitPointsFileName = "";
+        }
+
+        if (settings.symbolMapsDir.get() != null) {
+          storiesFileName = settings.soycDir.get() + "/stories" + permutationId
+              + ".xml.gz";
+
+          if (!(new File(storiesFileName).exists())) {
+            storiesFileName = settings.soycDir.get() + "/stories"
+                + permutationId + ".xml";
+          }
+          depFileName = settings.soycDir.get() + "/dependencies"
+              + permutationId + ".xml.gz";
+          if (!(new File(depFileName).exists())) {
+            depFileName = settings.soycDir.get() + "/dependencies"
+                + permutationId + ".xml";
+          }
+          splitPointsFileName = settings.soycDir.get() + "/splitPoints"
+              + permutationId + ".xml.gz";
+          if (!(new File(splitPointsFileName).exists())) {
+            splitPointsFileName = settings.soycDir.get() + "/splitPoints"
+                + permutationId + ".xml";
+          }
+        }
+
+        settings.displayDependencies = (new File(depFileName)).exists();
+        settings.displaySplitPoints = (new File(splitPointsFileName)).exists();
+
+        new File(settings.out.get()).mkdir();
+        if (settings.displayDependencies) {
+          /**
+           * handle dependencies
+           */
+          Map<String, Map<String, String>> dependencies = new TreeMap<String, Map<String, String>>();
+          DefaultHandler depHandler = parseXMLDocumentDependencies(dependencies);
+          SAXParserFactory depFactoryMain = SAXParserFactory.newInstance();
+          depFactoryMain.setNamespaceAware(true);
+          SAXParser saxParser = depFactoryMain.newSAXParser();
+          InputStream in = new FileInputStream(depFileName);
+          if (depFileName.endsWith(".gz")) {
+            in = new GZIPInputStream(in);
+          }
+          in = new BufferedInputStream(in);
+          saxParser.parse(in, depHandler);
+
+          makeTopLevelHtmlForPerm.makeDependenciesHtml(dependencies,
+              permutationId);
+        }
+
+        if (settings.displaySplitPoints) {
+          /**
+           * handle runAsync split points
+           */
+
+          DefaultHandler splitPointHandler = parseXMLDocumentSplitPoints(globalInformation);
+          SAXParserFactory splitPointsFactoryMain = SAXParserFactory.newInstance();
+          splitPointsFactoryMain.setNamespaceAware(true);
+
+          SAXParser saxParser = splitPointsFactoryMain.newSAXParser();
+          InputStream in = new FileInputStream(splitPointsFileName);
+          if (depFileName.endsWith(".gz")) {
+            in = new GZIPInputStream(in);
+          }
+          in = new BufferedInputStream(in);
+          saxParser.parse(in, splitPointHandler);
+        }
+
+        /**
+         * handle everything else
+         */
+
+        DefaultHandler handler = parseXMLDocumentSizeMap(globalInformation);
+        SAXParserFactory factoryMain = SAXParserFactory.newInstance();
+        factoryMain.setNamespaceAware(true);
+        SAXParser saxParser = factoryMain.newSAXParser();
+        InputStream in = new FileInputStream(storiesFileName);
+        if (storiesFileName.endsWith(".gz")) {
+          in = new GZIPInputStream(in);
+        }
+        in = new BufferedInputStream(in);
+        saxParser.parse(in, handler);
+
+        // add to "All Other Code" if none of the special categories apply
+        for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) {
+          updateAllOtherCodeType(breakdown.nameToCodeColl, globalInformation);
+        }
+        globalInformation.computePackageSizes();
+
+        // clean up the RPC categories
+        for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) {
+          foldInRPCHeuristic(breakdown.nameToCodeColl);
+        }
+
+        // generate all the html files
+        makeTopLevelHtmlForPerm.makeSplitStatusPages(permutationId);
+        makeTopLevelHtmlForPerm.makeLeftoverStatusPages(permutationId);
+        for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) {
+          DependencyLinker linker = chooseDependencyLinker(
+              makeTopLevelHtmlForPerm, breakdown);
+          makeHTMLFiles(makeTopLevelHtmlForPerm, breakdown, linker,
+              permutationId);
+        }
+        System.out.println("Finished creating reports for permutation.");
+      }
+      System.out.println("Finished creating reports. To see the dashboard, open index.html in your browser.");
+
+    } catch (ParserConfigurationException e) {
+      System.err.println("Could not parse document. " + e.getMessage());
+      System.exit(1);
+    } catch (SAXException e) {
+      System.err.println("Could not create SAX parser. " + e.getMessage());
+      System.exit(1);
+    } catch (FileNotFoundException e) {
+      System.err.println("Cannot open file " + e.getMessage());
+      System.exit(1);
+    } catch (IOException e) {
+      System.err.println("Error creating html file. " + e.getMessage());
+      System.exit(1);
+    } catch (Settings.ArgumentListException e) {
+      System.err.println(e.getMessage());
+      System.err.println("Usage: "
+          + "java com.google.gwt.soyc.SoycDashboard -resources dir -soycDir dir -symbolMaps dir [-out dir]");
+      System.err.println("(Legacy usage: "
+          + "java com.google.gwt.soyc.SoycDashboard options stories0.xml[.gz] [dependencies0.xml[.gz]] [splitpoints0.xml[.gz]])");
+      System.err.println("Options:");
+      System.err.println(Settings.settingsHelp());
+      System.exit(1);
+    }
+  }
+
+  private static Collection<SizeBreakdown> breakdownsForFragment(
+      Integer fragment, GlobalInformation globalInformation) {
+    List<SizeBreakdown> breakdowns = new ArrayList<SizeBreakdown>();
+    breakdowns.add(globalInformation.getTotalCodeBreakdown());
+    if (fragment == 0) {
+      breakdowns.add(globalInformation.getInitialCodeBreakdown());
+    }
+    if (fragment == (globalInformation.getNumSplitPoints() + 1)) {
+      breakdowns.add(globalInformation.getLeftoversBreakdown());
+    }
+    if (fragment >= 1 && fragment <= globalInformation.getNumSplitPoints()) {
+      breakdowns.add(globalInformation.splitPointCodeBreakdown(fragment));
+    }
+    return breakdowns;
+  }
+
+  private static DependencyLinker chooseDependencyLinker(
+      MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm, SizeBreakdown breakdown) {
+    if (breakdown == makeTopLevelHtmlForPerm.getGlobalInformation().getTotalCodeBreakdown()) {
+      return makeTopLevelHtmlForPerm.new DependencyLinkerForTotalBreakdown();
+    } else if (breakdown == makeTopLevelHtmlForPerm.getGlobalInformation().getInitialCodeBreakdown()) {
+      return makeTopLevelHtmlForPerm.new DependencyLinkerForInitialCode();
+    } else if (breakdown == makeTopLevelHtmlForPerm.getGlobalInformation().getLeftoversBreakdown()) {
+      return makeTopLevelHtmlForPerm.new DependencyLinkerForLeftoversFragment();
+    } else {
+      return makeTopLevelHtmlForPerm.new DependencyLinkerForExclusiveFragment();
+    }
+  }
+
+  /*
+   * cleans up the RPC code categories
+   */
+  private static void foldInRPCHeuristic(
+      final HashMap<String, CodeCollection> nameToCodeColl) {
+    /**
+     * Heuristic: this moves all classes that override serializable from RPC to
+     * "Other Code" *if* there is no RPC generated code, i.e., if the
+     * application really is not using RPC
+     */
+
+    if (nameToCodeColl.get("rpcGen").classes.size() == 0) {
+
+      for (String className : nameToCodeColl.get("rpcUser").classes) {
+
+        if ((!nameToCodeColl.get("widget").classes.contains(className))
+            && (!nameToCodeColl.get("jre").classes.contains(className))
+            && (!nameToCodeColl.get("gwtLang").classes.contains(className))) {
+          nameToCodeColl.get("allOther").classes.add(className);
+        }
+      }
+      nameToCodeColl.get("rpcUser").classes.clear();
+
+      for (String className : nameToCodeColl.get("rpcGwt").classes) {
+        if ((!nameToCodeColl.get("widget").classes.contains(className))
+            && (!nameToCodeColl.get("jre").classes.contains(className))
+            && (!nameToCodeColl.get("gwtLang").classes.contains(className))) {
+          nameToCodeColl.get("allOther").classes.add(className);
+        }
+      }
+      nameToCodeColl.get("rpcGwt").classes.clear();
+    }
+  }
+
+  /**
+   * Generates all the HTML files for one size breakdown.
+   * 
+   * @param makeTopLevelHtmlForPerm
+   * @param breakdown
+   * @param depLinker
+   * @param permutationId
+   * @throws IOException
+   */
+  private static void makeHTMLFiles(
+      MakeTopLevelHtmlForPerm makeTopLevelHtmlForPerm, SizeBreakdown breakdown,
+      DependencyLinker depLinker, String permutationId) throws IOException {
+    makeTopLevelHtmlForPerm.makePackageClassesHtmls(breakdown, depLinker,
+        permutationId);
+    makeTopLevelHtmlForPerm.makeCodeTypeClassesHtmls(breakdown, permutationId);
+    makeTopLevelHtmlForPerm.makeLiteralsClassesTableHtmls(breakdown,
+        permutationId);
+    makeTopLevelHtmlForPerm.makeBreakdownShell(breakdown, permutationId);
+    makeTopLevelHtmlForPerm.makeTopLevelShell(permutationId);
+  }
+
+  private static DefaultHandler parseXMLDocumentDependencies(
+      final Map<String, Map<String, String>> allDependencies) {
+    DefaultHandler handler = new DefaultHandler() {
+
+      // may want to create a class for this later
+      String curMethod;
+      Map<String, String> dependencies = new TreeMap<String, String>();
+      String graphExtends = null;
+      StringBuilder valueBuilder = new StringBuilder();
+
+      @Override
+      public void endElement(String uri, String localName, String qName) {
+        if (localName.compareTo("table") == 0) {
+          if (graphExtends != null) {
+            // Add in elements from the extended graph
+            for (Entry<String, String> entry : allDependencies.get(graphExtends).entrySet()) {
+              dependencies.put(entry.getKey(), entry.getValue());
+            }
+          }
+        }
+      }
+
+      @Override
+      public void startElement(String nsUri, String strippedName,
+          String tagName, final Attributes attributes) {
+
+        valueBuilder.delete(0, valueBuilder.length());
+
+        if (strippedName.compareTo("table") == 0
+            && (attributes.getValue("name") != null)) {
+          String name = attributes.getValue("name");
+          dependencies = new TreeMap<String, String>();
+          allDependencies.put(name, dependencies);
+          if (attributes.getValue("extends") != null) {
+            graphExtends = attributes.getValue("extends");
+            if (!allDependencies.containsKey(graphExtends)) {
+              throw new FormatException("Graph " + name
+                  + " extends an unknown graph " + graphExtends);
+            }
+          } else {
+            graphExtends = null;
+          }
+        } else 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)) {
+            dependencies.put(curMethod, curDepMethod);
+          }
+        }
+      }
+    };
+    return handler;
+  }
+
+  private static DefaultHandler parseXMLDocumentSizeMap(
+      final GlobalInformation globalInformation) {
+    return new DefaultHandler() {
+      int fragment = -1;
+
+      @Override
+      public void endElement(String uri, String localName, String qName) {
+        if (localName.compareTo("sizemap") == 0) {
+          fragment = -1;
+        }
+      }
+
+      @Override
+      public void startElement(String uri, String localName, String qName,
+          final Attributes attributes) {
+        if (localName.compareTo("sizemap") == 0) {
+          // starting a new size map
+          String fragString = attributes.getValue("fragment");
+          if (fragString == null) {
+            throw new FormatException();
+          }
+          try {
+            fragment = Integer.valueOf(fragString);
+          } catch (NumberFormatException e) {
+            throw new FormatException(e);
+          }
+          String sizeString = attributes.getValue("size");
+          if (sizeString == null) {
+            throw new FormatException();
+          }
+          int size;
+          try {
+            size = Integer.valueOf(sizeString);
+          } catch (NumberFormatException e) {
+            throw new FormatException(e);
+          }
+          for (SizeBreakdown breakdown : breakdownsForFragment(fragment,
+              globalInformation)) {
+            breakdown.sizeAllCode += size;
+          }
+        } else if (localName.compareTo("size") == 0) {
+          String type = attributes.getValue("type");
+          if (type == null) {
+            throw new FormatException();
+          }
+          String ref = attributes.getValue("ref");
+          if (ref == null) {
+            throw new FormatException();
+          }
+          String sizeString = attributes.getValue("size");
+          if (sizeString == null) {
+            throw new FormatException();
+          }
+          int size;
+          try {
+            size = Integer.valueOf(sizeString);
+          } catch (NumberFormatException e) {
+            throw new FormatException(e);
+          }
+          recordSize(type, ref, size, globalInformation);
+        }
+      }
+
+      private void accountForSize(SizeBreakdown breakdown, String refType,
+          String ref, int size, GlobalInformation globalInformation) {
+        if (refType.equals("string")) {
+          LiteralsCollection stringLiterals = breakdown.nameToLitColl.get("string");
+          stringLiterals.size += size;
+          stringLiterals.literals.add(ref);
+        } else if (refType.equals("var")) {
+          // Nothing to record, because no breakdown is provided for random
+          // variables
+        } else {
+          if (!refType.equals("type") && !refType.equals("method")) {
+            throw new FormatException();
+          }
+          String className = ref;
+          if (className.contains("::")) {
+            /*
+             * It's a method reference. Discard the method part.
+             */
+            int idx = className.indexOf(':');
+            className = className.substring(0, idx);
+          }
+
+          // derive the package name from the class
+          String packageName;
+          if (!globalInformation.getClassToPackage().containsKey(className)) {
+            packageName = className;
+            packageName = packageName.replaceAll("\\.[A-Z].*", "");
+            globalInformation.getClassToPackage().put(className, packageName);
+          } else {
+            packageName = globalInformation.getClassToPackage().get(className);
+          }
+          if (!globalInformation.getPackageToClasses().containsKey(packageName)) {
+            TreeSet<String> insertSet = new TreeSet<String>();
+            insertSet.add(className);
+            globalInformation.getPackageToClasses().put(packageName, insertSet);
+          } else {
+            globalInformation.getPackageToClasses().get(packageName).add(
+                className);
+          }
+
+          recordClassCategories(breakdown.nameToCodeColl, className,
+              packageName);
+
+          if (breakdown.classToSize.containsKey(className)) {
+            int newSize = breakdown.classToSize.get(className) + size;
+            breakdown.classToSize.put(className, newSize);
+          } else {
+            breakdown.classToSize.put(className, size);
+          }
+        }
+      }
+
+      private void recordClassCategories(
+          final HashMap<String, CodeCollection> nameToCodeColl,
+          String className, String packageName) {
+        if (packageName.startsWith("java")) {
+          nameToCodeColl.get("jre").classes.add(className);
+        } else if (packageName.startsWith("com.google.gwt.lang")) {
+          nameToCodeColl.get("gwtLang").classes.add(className);
+        }
+        if (className.contains("_CustomFieldSerializer")) {
+          nameToCodeColl.get("rpcUser").classes.add(className);
+        } else if (className.endsWith("_FieldSerializer")
+            || className.endsWith("_Proxy")
+            || className.endsWith("_TypeSerializer")) {
+          nameToCodeColl.get("rpcGen").classes.add(className);
+        }
+      }
+
+      private void recordSize(String refType, String ref, int size,
+          GlobalInformation globalInformation) {
+        for (SizeBreakdown breakdown : breakdownsForFragment(fragment,
+            globalInformation)) {
+          accountForSize(breakdown, refType, ref, size, globalInformation);
+        }
+      }
+    };
+  }
+
+  private static DefaultHandler parseXMLDocumentSplitPoints(
+      final GlobalInformation globalInformation) {
+
+    DefaultHandler handler = new DefaultHandler() {
+
+      private boolean inInitialLoadSequence = false;
+
+      @Override
+      public void endElement(String uri, String localName, String qName) {
+        if (localName.compareTo("initialesq") == 0) {
+          inInitialLoadSequence = false;
+        }
+      }
+
+      @Override
+      public void startElement(String nsUri, String strippedName,
+          String tagName, final Attributes attributes) {
+        if (strippedName.compareTo("splitpoint") == 0) {
+          parseSplitPoint(attributes);
+        } else if (strippedName.compareTo("initialseq") == 0) {
+          inInitialLoadSequence = true;
+        } else if (inInitialLoadSequence
+            && strippedName.compareTo("splitpointref") == 0) {
+          globalInformation.getSplitPointInitialLoadSequence().add(
+              parseSplitPointReference(attributes));
+        }
+      }
+
+      /*
+       * parses the split points
+       */
+      private void parseSplitPoint(final Attributes attributes) {
+        if (attributes.getValue("id") != null) {
+          String curSplitPoint = attributes.getValue("id");
+          if (attributes.getValue("location") != null) {
+            String curSplitPointLocation = attributes.getValue("location");
+
+            curSplitPointLocation = curSplitPointLocation.replaceAll("\\(L.*",
+                "");
+
+            globalInformation.getSplitPointToLocation().put(
+                Integer.parseInt(curSplitPoint), curSplitPointLocation);
+            globalInformation.incrementSplitPoints();
+          }
+        }
+      }
+
+      private Integer parseSplitPointReference(final Attributes attributes) {
+        String spString = attributes.getValue("id");
+        if (spString == null) {
+          throw new FormatException("Could not parse split point reference");
+        }
+        return Integer.valueOf(spString);
+      }
+
+    };
+    return handler;
+  }
+
+  /*
+   * assigns code to "all other code" if none of the special categories apply
+   */
+  private static void updateAllOtherCodeType(
+      final HashMap<String, CodeCollection> nameToCodeColl,
+      GlobalInformation globalInformation) {
+    // all classes not in any of the other categories
+    for (String className : globalInformation.getClassToPackage().keySet()) {
+      if ((!nameToCodeColl.get("widget").classes.contains(className))
+          && (!nameToCodeColl.get("rpcUser").classes.contains(className))
+          && (!nameToCodeColl.get("rpcGwt").classes.contains(className))
+          && (!nameToCodeColl.get("rpcGen").classes.contains(className))
+          && (!nameToCodeColl.get("jre").classes.contains(className))
+          && (!nameToCodeColl.get("gwtLang").classes.contains(className))) {
+        nameToCodeColl.get("allOther").classes.add(className);
+      }
+    }
+  }
+
+}