Reduces size of the compile report (soyc) for large projects with many
split points by:
- Using a common intern dictionary for methodDependency
reports from the same report.
- Sorting the interned dictionary by number of
references (so the most referenced strings have single
digit indices).
- Interning strings in the leftoversStatus report.
- Using single letter function names.
These changes amount to a further 20 to 30 percent reduction
in the size of the report for two large projects I've tried.
This also corrects a problem in the splitStatus and
leftoversStatus reports. The list of classes used
to construct the report was not sorted.
Review at http://gwt-code-reviews.appspot.com/1171801
Review by: kprobst@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9332 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/soyc/GlobalInformation.java b/dev/core/src/com/google/gwt/soyc/GlobalInformation.java
index 6758e78..4112404 100644
--- a/dev/core/src/com/google/gwt/soyc/GlobalInformation.java
+++ b/dev/core/src/com/google/gwt/soyc/GlobalInformation.java
@@ -30,7 +30,7 @@
public class GlobalInformation {
private static final SizeBreakdown[] EMPTY_SIZE_BREAKDOWN = new SizeBreakdown[0];
public Map<String, Map<String, String>> dependencies = null;
- private HashMap<String, String> classToPackage = new HashMap<String, String>();
+ private Map<String, String> classToPackage = new TreeMap<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(
@@ -76,7 +76,7 @@
*
* @return classToPackage
*/
- public final HashMap<String, String> getClassToPackage() {
+ public final Map<String, String> getClassToPackage() {
return classToPackage;
}
diff --git a/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
index 972ce0e..56297fa 100644
--- a/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
+++ b/dev/core/src/com/google/gwt/soyc/MakeTopLevelHtmlForPerm.java
@@ -66,7 +66,8 @@
*/
public class DependencyLinkerForLeftoversFragment implements DependencyLinker {
public String dependencyLinkForClass(String className) {
- return leftoversStatusFileName() + "#" + filename(className);
+ return leftoversStatusFileName() + "#"
+ + hashedFilenameFragment(className);
}
}
@@ -77,7 +78,7 @@
*/
public class DependencyLinkerForTotalBreakdown implements DependencyLinker {
public String dependencyLinkForClass(String className) {
- return splitStatusFileName() + "#" + filename(className);
+ return splitStatusFileName() + "#" + hashedFilenameFragment(className);
}
}
@@ -95,27 +96,280 @@
}
/**
- * Use this class to intern strings to save space in the generated HTML. After populating the
- * map, call getJs to create a JS array of all possible methods in this report.
+ * Use this class to intern strings to save space in the generated HTML. After
+ * populating the map, call getJs to create a JS array of all possible methods
+ * in this report.
*/
@SuppressWarnings("serial")
- private static class HtmlInterner extends LinkedHashMap<String, Integer> {
- int index = 0;
+ private class HtmlInterner {
+ // Hashes the interned string to the number of times this string is referenced.
+ Map<String, Integer> builder = new HashMap<String, Integer>();
+ // Hashes the interned string to its position in the final array of interned strings.
+ // Populated after the call to {@link #freeze()}
+ Map<String, Integer> frozen = null;
- public String getJs() {
- StringBuilder builder = new StringBuilder();
- builder.append(" var internedStrings = [\n");
- for (String key : keySet()) {
- builder.append("\"" + key + "\",");
+ /**
+ * Call this method after all calls to {@link #intern(String)} are complete.
+ * This routine then re-orders the interned calls in order of the number of
+ * times each string was referenced by intern() so that lower numbered index
+ * values represent more frequently referenced strings.
+ */
+ public void freeze() {
+ final int maxDigits = 9;
+ assert (frozen == null);
+ assert (builder.size() < Math.pow(10, maxDigits));
+
+ // order the interned values with the most referenced first.
+ String[] temp = new String[builder.size()];
+ int index = 0;
+ for (String key : builder.keySet()) {
+ temp[index++] = key.format("%0" + maxDigits + "d%s", builder.get(key), key);
}
- builder.append("];\n");
- return builder.toString();
+ builder = null;
+ Arrays.sort(temp);
+
+ // strip off the numeric prefix on the key to build the frozen hash table
+ index = 0;
+ frozen = new LinkedHashMap<String, Integer>();
+ for (int i = temp.length - 1; i >= 0; i--) {
+ frozen.put(temp[i].substring(maxDigits), index++);
+ }
}
+ /**
+ * Stores a string for later interning. Keeps track of the number of times a
+ * particular string is interned which will be used by {@link freeze()} to
+ * place the most frequently used strings at the beginning of the
+ * dictionary. After a call to {@link freeze()}, it is no longer valid to
+ * call this method.
+ *
+ * @param key string to be added to the intern dictionary.
+ */
public void intern(String key) {
- if (!containsKey(key)) {
- put(key, index++);
+ if (builder == null) {
+ throw new RuntimeException("freeze() already called.");
}
+ if (!builder.containsKey(key)) {
+ builder.put(key, 1);
+ } else {
+ int value = builder.get(key) + 1;
+ builder.put(key, value);
+ }
+ }
+
+ /**
+ * Displays a link for a split point that contains this code.
+ */
+ public void printHasCodeInSplitPoint(PrintWriter outFile, String className,
+ int sp) {
+ outFile.print("h(" + frozen.get(getPackageSubstring(className)) + ","
+ + frozen.get(getClassSubstring(className)) + "," + sp + ");");
+ }
+
+ /**
+ * Non specific message that there is code in an initial fragment.
+ */
+ public void printHasInitialFragment(PrintWriter outFile) {
+ outFile.print("f();");
+ }
+
+ /**
+ * Displays a link for code in an initial fragment.
+ */
+ public void printHasInitialFragment(PrintWriter outFile, String className) {
+ String packageName = getPackageSubstring(className);
+ outFile.print("g(" + frozen.get(packageName) + ","
+ + frozen.get(getClassSubstring(className)) + ","
+ + frozen.get(hashedFilenameFragment(packageName)) + ");");
+ }
+
+ public void printSomeCodeLeftover(PrintWriter outFile) {
+ outFile.print("i();");
+ }
+
+ /**
+ * Prints an h3 element with the class name and an anchor.
+ */
+ private void printClassHeader(PrintWriter outFile, String className) {
+ outFile.print("e(" + frozen.get(getPackageSubstring(className)) + ","
+ + frozen.get(getClassSubstring(className)) + ",'"
+ + hashedFilenameFragment(className) + "');");
+ }
+
+ /**
+ * Print out a single class dependency stack in the methodDependencies
+ * report.
+ */
+ private void printDependency(PrintWriter outFile,
+ Map<String, String> dependencies, String method, String depMethod) {
+ String nameArray = "[" + frozen.get(getPackageSubstring(method)) + ","
+ + frozen.get(getClassSubstring(method)) + ","
+ + frozen.get(getMethodSubstring(method)) + "]";
+ outFile.print("b(" + nameArray + ",");
+ outFile.print("[");
+ while (depMethod != null) {
+ String nextDep = dependencies.get(depMethod);
+ // The bottom of the stack frame is not interesting.
+ if (nextDep != null) {
+ String packageString = getPackageSubstring(depMethod);
+ String classString = getClassSubstring(depMethod);
+ String methodString = getMethodSubstring(depMethod);
+ outFile.print("[" + frozen.get(packageString) + ","
+ + frozen.get(classString) + "," + frozen.get(methodString) + "]");
+ }
+ depMethod = nextDep;
+ if (nextDep != null && dependencies.get(nextDep) != null) {
+ outFile.print(",");
+ }
+ }
+ outFile.print("]);");
+ }
+
+ /**
+ * Prints out a class header for the methodDependendies report.
+ */
+ private void printDependencyClassHeader(PrintWriter outFile,
+ String className) {
+ outFile.print("a(" + frozen.get(getPackageSubstring(className)) + ","
+ + frozen.get(getClassSubstring(className)) + ");");
+ }
+
+ /**
+ * Prints a JavaScript snippet that includes the dictionary of interned
+ * strings and methods to use interned strings to create lines in the
+ * report.
+ *
+ * Call this method after invoking {@link #freeze()}.
+ *
+ * @param outFile open file to write the data to.
+ */
+ private void printInternedDataAsJs(PrintWriter outFile) {
+ if (frozen == null) {
+ throw new RuntimeException("freeze() not called.");
+ }
+ outFile.println(" var internedStrings = [");
+ for (String key : frozen.keySet()) {
+ outFile.print("\"" + key + "\",");
+ }
+ outFile.println("];");
+
+
+ // array of split point descriptions
+ outFile.println(" var spl = [");
+ for (int sp = 1; sp <= globalInformation.getNumSplitPoints(); sp++) {
+ outFile.println(" '"
+ + globalInformation.getSplitPointToLocation().get(sp) + "',");
+ }
+ outFile.println(" ];");
+
+ // TODO(zundel): Most of this below is just inserting a fixed string into the code. It would
+ // be easier to read and maintain if we could store the fixed part in a flat file. Use some
+ // kind of HTML template?
+
+ // function to print a class header in the methodDependencies report
+ // see printDependencyClassHeader()
+ outFile.println(" function a(packageRef, classRef) {");
+ outFile.println(" var className = internedStrings[packageRef] + \".\" + "
+ + "internedStrings[classRef];");
+ outFile.println(" document.write(\"<a name='\" + className + \"'>\");");
+ outFile.println(" document.write(\"<h3 class='soyc-class-header'>Class: \" + className + "
+ + "\"</a></h3>\");");
+ outFile.println(" }");
+
+ // function to print a single dependency in the methodDependencies report
+ // see printDependency()
+ outFile.println(" function b(c, deps) {");
+ outFile.println(" document.write(\"<div class='main'>"
+ + "<a class='toggle soyc-call-stack-link' onclick='toggle.call(this)'>\");");
+ outFile.println(" document.write(\"<span class='calledBy'> Call stack: </span>\");");
+ outFile.println(" document.write(internedStrings[c[0]] + \".\" + internedStrings[c[1]] + "
+ + "\"::\" + internedStrings[c[2]] + \"</a>\");");
+ outFile.println(" document.write(\"<ul class='soyc-call-stack-list'>\");");
+ outFile.println(" for (var i = 0; i < deps.length ; i++) {");
+ outFile.println(" var s = deps[i];");
+ outFile.println(" document.write(\"<li>\" + internedStrings[s[0]] + \".\" + "
+ + "internedStrings[s[1]] + \"::\" + internedStrings[s[2]] + \"</li>\");");
+ outFile.println(" }");
+ outFile.println(" document.write(\"</ul></div>\");");
+ outFile.println(" }");
+
+ // leftovers status line
+ outFile.println(" function c(packageRef,classRef,packageHashRef) {");
+ outFile.println(" var packageName = internedStrings[packageRef];");
+ outFile.println(" var className = packageName + \".\" + internedStrings[classRef];");
+ outFile.println(" var d1 = 'methodDependencies-total-' + internedStrings[packageHashRef] "
+ + "+ '-" + getPermutationId() + ".html';");
+ outFile.println(" document.write(\"<ul class='soyc-excl'>\");");
+ outFile.println(" document.write(\"<li><a href='\" + d1 + \"#\" + className + \"'>"
+ + "See why it's live</a></li>\");");
+ outFile.println(" for (var sp = 1; sp <= "
+ + globalInformation.getNumSplitPoints() + "; sp++) {");
+ outFile.println(" var d2 = 'methodDependencies-sp' + sp + '-' + "
+ + "internedStrings[packageHashRef] + '-" + getPermutationId()
+ + ".html';");
+
+ outFile.println(" document.write(\"<li><a href='\" + d2 + \"#\" + className +\"'>"
+ + " See why it's not exclusive to s.p. #\" + sp + \" (\" + spl[sp - 1] + \")"
+ + "</a></li>\");");
+ outFile.println(" }");
+ outFile.println(" document.write(\"</ul>\");");
+ outFile.println(" }");
+
+ // leftovers status package header line
+ outFile.println(" function d(packageRef) {");
+ outFile.println(" document.write(\"<div class='soyc-pkg-break'>Package: \" + "
+ + "internedStrings[packageRef] + \"</div>\");");
+ outFile.println(" }");
+
+ // leftovers status class header line
+ outFile.println(" function e(packageRef,classRef,classHashRef) {");
+ outFile.println(" document.write(\"<a name='\" + classHashRef + \"'></a><h3>\" + "
+ + "internedStrings[packageRef] + \".\" + internedStrings[classRef] + \"</h3>\");");
+ outFile.println(" }");
+
+ // split point has a class with code in the initial fragment - no link
+ outFile.println(" function f() {");
+ outFile.println(" document.write(\"<p>Some code is included in the initial fragment"
+ + "</p>\");");
+ outFile.println(" }");
+
+ // split point has a class with code in the initial fragment
+ outFile.println(" function g(packageRef, classRef, packageHashRef) {");
+ outFile.println(" document.write(\"<p>Some code is included in the initial fragment "
+ + "(<a href='methodDependencies-initial-\" + internedStrings[packageHashRef] + \"-"
+ + getPermutationId()
+ + ".html#\" + internedStrings[packageRef] + \".\" + "
+ + "internedStrings[classRef] + \"'> See why</a>)</p>\");");
+ outFile.println(" }");
+
+ // split point has code from class
+ outFile.println(" function h(packageRef, classRef, sp) {");
+ outFile.println(" document.write(\"<p>Some code downloads with split point \" + sp + "
+ + "\": \" + spl[sp - 1] + \"</p>\");");
+ outFile.println(" }");
+
+ // some code is left over
+ outFile.println(" function i() {");
+ outFile.println(" document.write(\"<p>Some code is left over:</p>\");");
+ outFile.println(" }");
+ }
+
+ /**
+ * Prints links to each split point showing why a leftover fragment isn't
+ * exclusive.
+ */
+ private void printLeftoversStatus(PrintWriter outFile, String packageName,
+ String className) {
+ outFile.println("c(" + frozen.get(packageName) + ","
+ + frozen.get(getClassSubstring(className)) + ","
+ + frozen.get(hashedFilenameFragment(packageName)) + ");");
+ }
+
+ /**
+ * Prints a div containing the package name in a blue block.
+ */
+ private void printPackageHeader(PrintWriter outFile, String packageName) {
+ outFile.print("d(" + frozen.get(packageName) + ");");
}
}
@@ -280,7 +534,9 @@
outFile.println("<div id=\"hd\" class=\"g-section g-tpl-50-50 g-split\">");
outFile.println("<div class=\"g-unit g-first\">");
outFile.println("<p>");
- outFile.println("<a href=\"index.html\" id=\"gwt-logo\" class=\"soyc-ir\"><span>Google Web Toolkit</span></a>");
+ outFile.println("<a href=\"index.html\" id=\"gwt-logo\" class=\"soyc-ir\">");
+ outFile.println("<span>Google Web Toolkit</span>");
+ outFile.println("</a>");
outFile.println("</p>");
outFile.println("</div>");
outFile.println("<div class=\"g-unit\">");
@@ -313,7 +569,7 @@
* Convert a potentially long string into a short file name. The current
* implementation simply hashes the long name.
*/
- private static String filename(String longFileName) {
+ private static String hashedFilenameFragment(String longFileName) {
try {
return Util.computeStrongName(longFileName.getBytes(Util.DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
@@ -359,10 +615,11 @@
String popupName = "packageBreakdownPopup";
String popupTitle = "Package breakdown";
String popupBody = "The package breakdown blames pieces of JavaScript "
- + "code on Java packages wherever possible. Note that this is not possible for all code, so the sizes "
- + "of the packages here will not normally add up to the full code size. More specifically, the sum will "
- + "exclude strings, whitespace, and a few pieces of JavaScript code that are produced during compilation "
- + "but cannot be attributed to any Java package.";
+ + "code on Java packages wherever possible. Note that this is not possible for all "
+ + "code, so the sizes of the packages here will not normally add up to the full code "
+ + "size. More specifically, the sum will exclude strings, whitespace, and a few pieces "
+ + "of JavaScript code that are produced during compilation but cannot be attributed to "
+ + "any Java package.";
outFile.println("<h2>");
addPopupLink(outFile, popupName, popupTitle, null);
@@ -375,8 +632,8 @@
popupTitle = "Code Type Breakdown";
popupBody = "The code type breakdown breaks down the JavaScript code according to its "
+ "type or function. For example, it tells you how much of your code can be attributed to "
- + "JRE, GWT-RPC, etc. As above, strings and some other JavaScript snippets are not included "
- + "in the breakdown.";
+ + "JRE, GWT-RPC, etc. As above, strings and some other JavaScript snippets are not "
+ + "included in the breakdown.";
outFile.println("<h2>");
addPopupLink(outFile, popupName, popupTitle, null);
outFile.println("</h2>");
@@ -647,16 +904,32 @@
outFile.println("<p>These classes have some leftover code, neither initial nor "
+ "exclusive to any split point:</p>");
String curPackageName = "";
+ HtmlInterner interner = new HtmlInterner();
+
+ for (String className : globalInformation.getClassToPackage().keySet()) {
+ String packageName = globalInformation.getClassToPackage().get(className);
+ interner.intern(packageName);
+ interner.intern(getClassSubstring(className));
+ interner.intern(hashedFilenameFragment(packageName));
+ }
+ interner.freeze();
+
+ outFile.println("<script language=\"javascript\">");
+ interner.printInternedDataAsJs(outFile);
+
for (String className : globalInformation.getClassToPackage().keySet()) {
String packageName = globalInformation.getClassToPackage().get(className);
if (packageName.compareTo("") == 0
|| packageName.compareTo(curPackageName) != 0) {
curPackageName = packageName;
- outFile.println("<div class='soyc-pkg-break'>Package: " + packageName + "</div>");
+ interner.printPackageHeader(outFile, packageName);
}
- outFile.println("<a name=\"" + filename(className) + "\"></a><h3>" + className + "</h3>");
- addLefttoversStatus(className, packageName, outFile);
+ interner.printClassHeader(outFile, className);
+ if (globalInformation.dependencies != null) {
+ interner.printLeftoversStatus(outFile, packageName, className);
+ }
}
+ outFile.println("</script>");
addStandardHtmlEnding(outFile);
outFile.close();
}
@@ -692,13 +965,14 @@
*/
public void makePackageClassesHtmls(SizeBreakdown breakdown,
DependencyLinker depLinker) throws IOException {
- PrintWriter outFile = new PrintWriter(
- getOutFile(classesInPackageFileName(breakdown, getPermutationId())));
+ PrintWriter outFile = new PrintWriter(getOutFile(classesInPackageFileName(
+ breakdown, getPermutationId())));
addStandardHtmlProlog(outFile, "Classes in " + breakdown.getDescription(),
"Classes in " + breakdown.getDescription(),
headerLineForBreakdown(breakdown));
- String[] packageNames = globalInformation.getPackageToClasses().keySet().toArray(new String[0]);
+ String[] packageNames = globalInformation.getPackageToClasses().keySet().toArray(
+ new String[0]);
Arrays.sort(packageNames);
for (String packageName : packageNames) {
TreeMap<Integer, Set<String>> sortedClasses = new TreeMap<Integer, Set<String>>(
@@ -734,8 +1008,8 @@
outFile.print("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.print("<thead>");
- outFile.print("<a name=\"" + filename(packageName) + "\"></a><th>Package: "
- + packageName + "</th>");
+ outFile.print("<a name=\"" + hashedFilenameFragment(packageName)
+ + "\"></a><th>Package: " + packageName + "</th>");
outFile.print("<th>Size <span class=\"soyc-th-units\">(Bytes)</span></th>");
outFile.print("</thead>");
@@ -774,42 +1048,50 @@
public void makeSplitStatusPages() throws IOException {
PrintWriter outFile = new PrintWriter(getOutFile(splitStatusFileName()));
- addStandardHtmlProlog(outFile, "Split point status", "Split point status", "");
+ addStandardHtmlProlog(outFile, "Split point status", "Split point status",
+ "");
outFile.println("<div id=\"bd\">");
- String curPackageName = "";
-
+ HtmlInterner interner = new HtmlInterner();
for (String className : globalInformation.getClassToPackage().keySet()) {
String packageName = globalInformation.getClassToPackage().get(className);
+ interner.intern(packageName);
+ interner.intern(getClassSubstring(className));
+ interner.intern(hashedFilenameFragment(packageName));
+ }
+ interner.freeze();
+
+ outFile.println("<script language=\"javascript\">");
+ interner.printInternedDataAsJs(outFile);
+
+ String curPackageName = "";
+ for (String className : globalInformation.getClassToPackage().keySet()) {
+
+ String packageName = globalInformation.getClassToPackage().get(className);
if (packageName.compareTo("") == 0
|| packageName.compareTo(curPackageName) != 0) {
curPackageName = packageName;
- outFile.println("<div class='soyc-pkg-break'>Package: " + packageName + "</div>");
+ interner.printPackageHeader(outFile, packageName);
}
- outFile.println("<a name=\"" + filename(className) + "\"></a><h3>"
- + className + "</h3>");
+ interner.printClassHeader(outFile, className);
if (globalInformation.getInitialCodeBreakdown().classToSize.containsKey(className)) {
if (globalInformation.dependencies != null) {
- outFile.println("<p>Some code is included in the initial fragment (<a href=\""
- + dependenciesFileName("initial", packageName)
- + "#"
- + className
- + "\">see why</a>)</p>");
+ interner.printHasInitialFragment(outFile, className);
} else {
- outFile.println("<p>Some code is included in the initial fragment</p>");
+ interner.printHasInitialFragment(outFile);
}
}
for (int sp : splitPointsWithClass(className)) {
- outFile.println("<p>Some code downloads with split point " + sp + ": "
- + globalInformation.getSplitPointToLocation().get(sp) + "</p>");
+ interner.printHasCodeInSplitPoint(outFile, className, sp);
}
if (globalInformation.getLeftoversBreakdown().classToSize.containsKey(className)) {
- outFile.println("<p>Some code is left over:</p>");
- addLefttoversStatus(className, packageName, outFile);
+ interner.printSomeCodeLeftover(outFile);
+ interner.printLeftoversStatus(outFile, packageName, className);
}
}
+ outFile.println("</script>");
addStandardHtmlEnding(outFile);
outFile.close();
@@ -918,23 +1200,6 @@
outFile.close();
}
- private void addLefttoversStatus(String className, String packageName, PrintWriter outFile) {
- outFile.println("<ul class=\"soyc-excl\">");
- if (globalInformation.dependencies != null) {
- outFile.println("<li><a href=\""
- + dependenciesFileName("total", packageName) + "#" + className
- + "\">See why it's live</a></li>");
- for (int sp = 1; sp <= globalInformation.getNumSplitPoints(); sp++) {
- outFile.println("<li><a href=\""
- + dependenciesFileName("sp" + sp, packageName) + "#" + className
- + "\">See why it's not exclusive to s.p. #" + sp + " ("
- + globalInformation.getSplitPointToLocation().get(sp)
- + ")</a></li>");
- }
- }
- outFile.println("</ul>");
- }
-
private void addPopup(PrintWriter outFile, String popupName,
String popupTitle, String popupBody) {
outFile.println("<div class=\"soyc-popup\" id=\"" + popupName + "\">");
@@ -960,8 +1225,9 @@
* Returns a file name for the dependencies list.
*/
private String dependenciesFileName(String depGraphName, String packageName) {
- return "methodDependencies-" + depGraphName + "-" + filename(packageName)
- + "-" + getPermutationId() + ".html";
+ return "methodDependencies-" + depGraphName + "-"
+ + hashedFilenameFragment(packageName) + "-" + getPermutationId()
+ + ".html";
}
/**
@@ -1266,47 +1532,9 @@
Map<String, String> dependencies) throws IOException {
String curPackageName = "";
- // Separate out the packages to write them into different HTML files.
- String packageName = "";
- List<String> classesInPackage = new ArrayList<String>();
- 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]
- packageName = method.replaceAll("\\.\\p{Upper}.*", "");
-
- if (curPackageName.compareTo("") == 0) {
- curPackageName = packageName;
- } else if (curPackageName.compareTo(packageName) != 0) {
- makeDependenciesInternedHtml(depGraphName, curPackageName,
- classesInPackage, dependencies);
- classesInPackage = new ArrayList<String>();
- curPackageName = packageName;
- }
- classesInPackage.add(method);
- }
- if (classesInPackage.size() > 0) {
- makeDependenciesInternedHtml(depGraphName, curPackageName,
- classesInPackage, dependencies);
- }
- }
-
- /**
- * Produces an HTML file that displays dependencies.
- *
- * @param depGraphName name of dependency graph
- * @param dependencies map of dependencies
- * @throws IOException
- */
- private void makeDependenciesInternedHtml(String depGraphName,
- String packageName, List<String> classesInPackage,
- Map<String, String> dependencies) throws IOException {
- String depGraphDescription = inferDepGraphDescription(depGraphName);
- PrintWriter outFile = null;
- String curClassName = "";
-
HtmlInterner interner = new HtmlInterner();
- for (String reportMethod : classesInPackage) {
+ for (String reportMethod : dependencies.keySet()) {
interner.intern(getPackageSubstring(reportMethod));
interner.intern(getClassSubstring(reportMethod));
interner.intern(getMethodSubstring(reportMethod));
@@ -1319,6 +1547,53 @@
depMethod = dependencies.get(depMethod);
}
}
+ interner.freeze();
+
+ // Write out the interned data values as a script element
+ String jsFileName = "methodDependencies-" + depGraphName + "-"
+ + getPermutationId() + ".js";
+ PrintWriter outFile = new PrintWriter(getOutFile(jsFileName));
+ interner.printInternedDataAsJs(outFile);
+ outFile.close();
+
+ // Separate out the packages to write them into different HTML files.
+ String packageName = "";
+ List<String> classesInPackage = new ArrayList<String>();
+ 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]
+ packageName = method.replaceAll("\\.\\p{Upper}.*", "");
+
+ if (curPackageName.compareTo("") == 0) {
+ curPackageName = packageName;
+ } else if (curPackageName.compareTo(packageName) != 0) {
+ makeDependenciesInternedHtml(depGraphName, curPackageName,
+ classesInPackage, dependencies, interner, jsFileName);
+ classesInPackage = new ArrayList<String>();
+ curPackageName = packageName;
+ }
+ classesInPackage.add(method);
+ }
+ if (classesInPackage.size() > 0) {
+ makeDependenciesInternedHtml(depGraphName, curPackageName,
+ classesInPackage, dependencies, interner, jsFileName);
+ }
+ }
+
+ /**
+ * Produces an HTML file that displays dependencies.
+ *
+ * @param depGraphName name of dependency graph
+ * @param dependencies map of dependencies
+ * @throws IOException
+ */
+ private void makeDependenciesInternedHtml(String depGraphName,
+ String packageName, List<String> classesInPackage,
+ Map<String, String> dependencies, HtmlInterner interner, String jsFileName)
+ throws IOException {
+ String depGraphDescription = inferDepGraphDescription(depGraphName);
+ PrintWriter outFile = null;
+ String curClassName = "";
String outFileName = dependenciesFileName(depGraphName, packageName);
outFile = new PrintWriter(getOutFile(outFileName));
@@ -1329,30 +1604,8 @@
"Method Dependencies for " + depGraphDescription, "Showing Package: "
+ packageDescription);
- // Write out the data values in the script
- outFile.println("<script language=\"javascript\">");
- outFile.println(interner.getJs());
- // function to print a class header
- outFile.println(" function showC(packageRef, classRef) {");
- outFile.println(" var className = internedStrings[packageRef] + \".\" + internedStrings[classRef];");
- outFile.println(" document.write(\"<a name='\" + className + \"'>\");");
- outFile.println(" document.write(\"<h3 class='soyc-class-header'>Class: \" + className + \"</a></h3>\");");
- outFile.println(" }");
- // function to print a dependency
- outFile.println(" function showD(c, deps) {");
- outFile.println(" document.write(\"<div class='main'><a class='toggle soyc-call-stack-link' "
- + "onclick='toggle.call(this)'><span class='calledBy'> Call stack: </span>\");");
- outFile.println(" document.write(internedStrings[c[0]] + \".\" + internedStrings[c[1]] + \"::\" + "
- + "internedStrings[c[2]] + \"</a>\");");
- outFile.println(" document.write(\"<ul class='soyc-call-stack-list'>\");");
- outFile.println(" for (var i = 0; i < deps.length ; i++) {");
- outFile.println(" var s = deps[i];");
- outFile.println(" document.write(\"<li>\" + internedStrings[s[0]] + \".\" + internedStrings[s[1]] +"
- + "\"::\" + internedStrings[s[2]] + \"</li>\");");
- outFile.println(" }");
- outFile.println(" document.write(\"</ul></div>\");");
- outFile.println(" }");
- outFile.println("</script>");
+ outFile.print("<script src=\"" + jsFileName
+ + "\" language=\"javascript\" ></script>");
// Write out the HTML
outFile.print("<script>");
@@ -1364,31 +1617,9 @@
String depMethod = dependencies.get(method);
if (curClassName.compareTo(className) != 0) {
curClassName = className;
- outFile.print("showC(" + interner.get(getPackageSubstring(className))
- + "," + interner.get(getClassSubstring(className)) + ");");
+ interner.printDependencyClassHeader(outFile, className);
}
- String nameArray = "[" + interner.get(getPackageSubstring(method)) + ","
- + interner.get(getClassSubstring(method)) + ","
- + interner.get(getMethodSubstring(method)) + "]";
- outFile.print("showD(" + nameArray + ",");
- outFile.print(" [");
- while (depMethod != null) {
- String nextDep = dependencies.get(depMethod);
- // The bottom of the stack frame is not interesting.
- if (nextDep != null) {
- String packageString = getPackageSubstring(depMethod);
- String classString = getClassSubstring(depMethod);
- String methodString = getMethodSubstring(depMethod);
- outFile.print("[" + interner.get(packageString) + ","
- + interner.get(classString) + "," + interner.get(methodString)
- + "]");
- }
- depMethod = nextDep;
- if (nextDep != null) {
- outFile.print(",");
- }
- }
- outFile.print(" ]);");
+ interner.printDependency(outFile, dependencies, method, depMethod);
}
outFile.println("</script>");
@@ -1444,8 +1675,9 @@
}
Set<String> packageNames = sortedPackages.get(size);
for (String packageName : packageNames) {
- String drillDownFileName = classesInPackageFileName(breakdown, getPermutationId())
- + "#" + filename(packageName);
+ String drillDownFileName = classesInPackageFileName(breakdown,
+ getPermutationId())
+ + "#" + hashedFilenameFragment(packageName);
float perc = (size / sumSize) * 100;
outFile.println("<tr>");
outFile.println("<td><a href=\"" + drillDownFileName