blob: 94873c6376f38f499c8df85cb1b59aec5087636a [file] [log] [blame]
/*
* 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.core.ext.soyc.impl.SizeMapRecorder;
import com.google.gwt.dev.util.Util;
import com.google.gwt.soyc.io.OutputDirectory;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A utility to make all the HTML files for one permutation.
*/
public class MakeTopLevelHtmlForPerm {
/**
* 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 packageName = globalInformation.getClassToPackage().get(className);
assert packageName != null;
return dependenciesFileName("initial", packageName) + "#" + className;
}
}
/**
* A dependency linker for the leftovers fragment. It links to leftovers
* status pages.
*/
public class DependencyLinkerForLeftoversFragment implements DependencyLinker {
public String dependencyLinkForClass(String className) {
return leftoversStatusFileName(className);
}
}
/**
* 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) {
return splitStatusFileName(className);
}
}
/**
* A dependency linker that never links to anything.
*/
public static class NullDependencyLinker implements DependencyLinker {
public String dependencyLinkForClass(String className) {
return null;
}
}
interface DependencyLinker {
String dependencyLinkForClass(String className);
}
/**
* 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 escapeXml(String unescaped) {
return SizeMapRecorder.escapeXml(unescaped);
}
public static void makeTopLevelHtmlForAllPerms(
Map<String, List<String>> allPermsInfo, OutputDirectory outDir)
throws IOException {
PrintWriter outFile = new PrintWriter(outDir.getOutputStream("index.html"));
addStandardHtmlProlog(outFile, "Compile report", "Compile report",
"Overview of permutations");
outFile.println("<ul>");
// in order to print these in ascending order, we have to sort by
// integers
SortedSet<Integer> sortedPermIds = new TreeSet<Integer>();
for (String permutationId : allPermsInfo.keySet()) {
sortedPermIds.add(Integer.parseInt(permutationId));
}
for (Integer sortedPermId : sortedPermIds) {
String permutationId = Integer.toString(sortedPermId);
List<String> permutationInfoList = allPermsInfo.get(permutationId);
outFile.print("<li><a href=\"SoycDashboard" + "-" + permutationId
+ "-index.html\">Permutation " + permutationId);
if (permutationInfoList.size() > 0) {
for (String desc : permutationInfoList) {
outFile.println(" (" + desc + ")");
}
outFile.println("</a></li>");
} else {
outFile.println("</a>");
}
}
outFile.println("</ul>");
addStandardHtmlEnding(outFile);
outFile.close();
}
private static void addSmallHtmlProlog(final PrintWriter outFile, String title) {
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>");
outFile.println(title);
outFile.println("</title>");
outFile.println("<style type=\"text/css\" media=\"screen\">");
outFile.println("@import url('goog.css');");
outFile.println("@import url('inlay.css');");
outFile.println("@import url('soyc.css');");
outFile.println("</style>");
outFile.println("</head>");
}
private static void addStandardHtmlEnding(final PrintWriter out) {
out.println("</div>");
out.println("</body>");
out.println("</html>");
}
private static void addStandardHtmlProlog(final PrintWriter outFile,
String title, String header1, String header2) {
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("<script type=\"text/javascript\">");
outFile.println("function show(elementName)");
outFile.println("{");
outFile.println("hp = document.getElementById(elementName);");
outFile.println("hp.style.visibility = \"Visible\";");
outFile.println("}");
outFile.println("function hide(elementName)");
outFile.println("{");
outFile.println("hp = document.getElementById(elementName);");
outFile.println("hp.style.visibility = \"Hidden\";");
outFile.println("}");
outFile.println("</script>");
outFile.println("<title>");
outFile.println(title);
outFile.println("</title>");
outFile.println("<style type=\"text/css\" media=\"screen\">");
outFile.println("@import url('goog.css');");
outFile.println("@import url('inlay.css');");
outFile.println("@import url('soyc.css');");
outFile.println("</style>");
outFile.println("</head>");
outFile.println("<body>");
outFile.println("<div class=\"g-doc\">");
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("</p>");
outFile.println("</div>");
outFile.println("<div class=\"g-unit\">");
outFile.println("</div>");
outFile.println("</div>");
outFile.println("<div id=\"soyc-appbar-lrg\">");
outFile.println("<div class=\"g-section g-tpl-75-25 g-split\">");
outFile.println("<div class=\"g-unit g-first\">");
outFile.println("<h1>" + header1 + "</h1>");
outFile.println("</div>");
outFile.println("<div class=\"g-unit\"></div>");
outFile.println("</div>");
outFile.println("</div>");
outFile.println("<div id=\"bd\">");
outFile.println("<h2>" + header2 + "</h2>");
}
private static String classesInPackageFileName(SizeBreakdown breakdown,
String packageName, String permutationId) {
return breakdown.getId() + "_" + filename(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 final GlobalInformation globalInformation;
private final OutputDirectory outDir;
MakeTopLevelHtmlForPerm(GlobalInformation globalInformation,
OutputDirectory outDir) {
this.globalInformation = globalInformation;
this.outDir = outDir;
}
public void makeBreakdownShell(SizeBreakdown breakdown) throws IOException {
Map<String, CodeCollection> nameToCodeColl = breakdown.nameToCodeColl;
Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl;
String packageBreakdownFileName = makePackageHtml(breakdown);
String codeTypeBreakdownFileName = makeCodeTypeHtml(breakdown,
nameToCodeColl, nameToLitColl);
PrintWriter outFile = new PrintWriter(getOutFile(shellFileName(breakdown,
getPermutationId())));
addStandardHtmlProlog(outFile, "Application breakdown analysis",
"Application breakdown analysis", "");
outFile.println("<div id=\"bd\"><h2><a style=\"cursor:default;\" onMouseOver=\"show('packageBreakdownPopup');\" " +
"onMouseOut=\"hide('packageBreakdownPopup');\">Package breakdown</a></h2></div>");
outFile.println("<div id=\"packageBreakdownPopup\" style=\"visibility:hidden; position:absolute;\">");
outFile.println("<table bgcolor=\"#c5d7ef\" width=\"200\">");
outFile.println("<tr><td><b>Package breakdown</b></td></tr>");
outFile.println("<tr><td bgcolor=\"#e5ecf9\">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.</td></tr></table></div>");
outFile.println("<iframe class='soyc-iframe-package' src=\""
+ packageBreakdownFileName + "\" scrolling=auto></iframe>");
outFile.println("<h2><a style=\"cursor:default;\" onMouseOver=\"show('codeTypeBreakdownPopup');\" " +
"onMouseOut=\"hide('codeTypeBreakdownPopup');\">Code type breakdown</a></h2>");
outFile.println("<div id=\"codeTypeBreakdownPopup\" style=\"visibility:hidden; position:absolute;\">");
outFile.println("<table bgcolor=\"#c5d7ef\" width=\"200\">");
outFile.println("<tr><td><b>Code type breakdown</b></td></tr>");
outFile.println("<tr><td bgcolor=\"#e5ecf9\">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.</td></tr></table></div>");
outFile.println("<iframe class='soyc-iframe-code' src=\""
+ codeTypeBreakdownFileName + "\" scrolling=auto></iframe>");
outFile.println("</div>");
addStandardHtmlEnding(outFile);
outFile.close();
}
public void makeCodeTypeClassesHtmls(SizeBreakdown breakdown)
throws IOException {
HashMap<String, CodeCollection> nameToCodeColl = breakdown.nameToCodeColl;
for (String codeType : nameToCodeColl.keySet()) {
// construct file name
String outFileName = breakdown.getId() + "_" + codeType + "-"
+ getPermutationId() + "Classes.html";
float sumSize = 0f;
TreeMap<Integer, Set<String>> sortedClasses = new TreeMap<Integer, Set<String>>(
Collections.reverseOrder());
for (String className : nameToCodeColl.get(codeType).classes) {
if (breakdown.classToSize.containsKey(className)) {
int curSize = 0;
if (breakdown.classToSize.containsKey(className)) {
curSize = breakdown.classToSize.get(className);
}
if (curSize != 0) {
if (sortedClasses.containsKey(curSize)) {
Set<String> existingSet = sortedClasses.get(curSize);
existingSet.add(className);
sortedClasses.put(curSize, existingSet);
} else {
Set<String> newSet = new TreeSet<String>();
newSet.add(className);
sortedClasses.put(curSize, newSet);
}
sumSize += curSize;
}
}
}
final PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
addStandardHtmlProlog(outFile, "Classes in package " + codeType,
"Classes in package " + codeType, headerLineForBreakdown(breakdown));
outFile.println("<table class=\"soyc-table\">");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-splitpoint-type-col\">");
outFile.println("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>Code type</th>");
outFile.println("<th>Size <span class=\"soyc-th-units\">(Bytes)</span></th>");
outFile.println("</thead>");
for (Integer size : sortedClasses.keySet()) {
Set<String> classNames = sortedClasses.get(size);
for (String className : classNames) {
float perc = ((float) size / sumSize) * 100;
outFile.println("<tr>");
outFile.println("<td>" + className + "</a></td>");
outFile.println("<td>");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + perc
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println(size + " (" + formatNumber(perc) + "%)");
outFile.println("</td>");
outFile.println("</tr>");
}
}
addStandardHtmlEnding(outFile);
outFile.close();
}
}
public void makeDependenciesHtml() throws IOException {
for (String depGraphName : globalInformation.dependencies.keySet()) {
makeDependenciesHtml(depGraphName,
globalInformation.dependencies.get(depGraphName));
}
}
public void makeLeftoverStatusPages() throws IOException {
for (String className : globalInformation.getClassToPackage().keySet()) {
makeLeftoversStatusPage(className);
}
}
public void makeLiteralsClassesTableHtmls(SizeBreakdown breakdown)
throws IOException {
Map<String, LiteralsCollection> nameToLitColl = breakdown.nameToLitColl;
for (String literalType : nameToLitColl.keySet()) {
String outFileName = literalType + "-" + getPermutationId() + "Lits.html";
final PrintWriter outFile = new PrintWriter(getOutFile(breakdown.getId()
+ "_" + outFileName));
addStandardHtmlProlog(outFile, "Literals of type " + literalType,
"Literals of type " + literalType, headerLineForBreakdown(breakdown));
outFile.println("<table width=\"80%\" style=\"font-size: 11pt;\" bgcolor=\"white\">");
for (String literal : nameToLitColl.get(literalType).literals) {
if (literal.trim().length() == 0) {
literal = "[whitespace only string]";
}
String escliteral = escapeXml(literal);
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) throws IOException {
for (String packageName : globalInformation.getPackageToClasses().keySet()) {
TreeMap<Integer, Set<String> > sortedClasses = new TreeMap<Integer, Set<String> >(
Collections.reverseOrder());
int sumSize = 0;
for (String className : globalInformation.getPackageToClasses().get(
packageName)) {
int curSize = 0;
if (!breakdown.classToSize.containsKey(className)) {
// This class not present in this code collection
} else {
curSize = breakdown.classToSize.get(className);
}
if (curSize != 0f) {
if (sortedClasses.containsKey(curSize)) {
Set<String> existingSet = sortedClasses.get(curSize);
existingSet.add(className);
sortedClasses.put(curSize, existingSet);
} else {
Set<String> newSet = new TreeSet<String>();
newSet.add(className);
sortedClasses.put(curSize, newSet);
}
sumSize += curSize;
}
}
PrintWriter outFile = new PrintWriter(
getOutFile(classesInPackageFileName(breakdown, packageName,
getPermutationId())));
addStandardHtmlProlog(outFile, "Classes in package " + packageName,
"Classes in package " + packageName,
headerLineForBreakdown(breakdown));
outFile.println("<table class=\"soyc-table\">");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-splitpoint-type-col\">");
outFile.println("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>Package</th>");
outFile.println("<th>Size <span class=\"soyc-th-units\">(Bytes)</span></th>");
outFile.println("</thead>");
for (Integer size : sortedClasses.keySet()) {
Set<String> classNames = sortedClasses.get(size);
for (String className : classNames) {
String drillDownFileName = depLinker.dependencyLinkForClass(className);
float perc = ((float) size / (float) sumSize) * 100;
outFile.println("<tr>");
if (drillDownFileName == null) {
outFile.println("<td>" + className + "</td>");
} else {
outFile.println("<td><a href=\"" + drillDownFileName
+ "\" target=\"_top\">" + className + "</a></td>");
}
outFile.println("<td>");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + perc
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println(size + " (" + formatNumber(perc) + "%)");
outFile.println("</td>");
outFile.println("</tr>");
}
}
outFile.println("</table>");
addStandardHtmlEnding(outFile);
outFile.close();
}
}
public void makeSplitStatusPages() throws IOException {
for (String className : globalInformation.getClassToPackage().keySet()) {
makeSplitStatusPage(className);
}
}
public void makeTopLevelShell() throws IOException {
String permutationId = getPermutationId();
PrintWriter outFile = new PrintWriter(getOutFile("SoycDashboard" + "-"
+ getPermutationId() + "-index.html"));
addStandardHtmlProlog(outFile, "Compile report: Permutation "
+ permutationId, "Compile report: Permutation " + permutationId, "");
outFile.println("<div id=\"bd\">");
outFile.println("<div id=\"soyc-summary\" class=\"g-section\">");
outFile.println("<dl>");
outFile.println("<dt>Full code size</dt>");
outFile.println("<dd class=\"value\">"
+ globalInformation.getTotalCodeBreakdown().sizeAllCode
+ " Bytes</dd>");
outFile.println("<dd class=\"report\"><a href=\"total-" + permutationId
+ "-overallBreakdown.html\">Report</a></dd>");
outFile.println("</dl>");
outFile.println("<dl>");
outFile.println("<dt>Initial download size</dt>");
// TODO(kprobst) -- add percentage here: (48%)</dd>");
outFile.println("<dd class=\"value\">"
+ globalInformation.getInitialCodeBreakdown().sizeAllCode + " Bytes</dd>");
outFile.println("<dd class=\"report\"><a href=\"initial-" + permutationId
+ "-overallBreakdown.html\">Report</a></dd>");
outFile.println("</dl>");
outFile.println("<dl>");
outFile.println("<dt>Left over code</dt>");
outFile.println("<dd class=\"value\">"
+ globalInformation.getLeftoversBreakdown().sizeAllCode + " Bytes</dd>");
outFile.println("<dd class=\"report\"><a href=\"leftovers-" + permutationId
+ "-overallBreakdown.html\">Report</a></dd>");
outFile.println("</dl>");
outFile.println("</div>");
outFile.println("<table id=\"soyc-table-splitpoints\" class=\"soyc-table\">");
outFile.println("<caption>");
outFile.println("<strong>Split Points</strong>");
outFile.println("</caption>");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-splitpoint-number-col\">");
outFile.println("<col id=\"soyc-splitpoint-location-col\">");
outFile.println("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>#</th>");
outFile.println("<th>Location</th>");
outFile.println("<th>Size <span class=\"soyc-th-units\">(Bytes)</span></th>");
outFile.println("</thead>");
outFile.println("<tbody>");
if (globalInformation.getSplitPointToLocation().size() >= 1) {
int numSplitPoints = globalInformation.getSplitPointToLocation().size();
int maxSize = globalInformation.getTotalCodeBreakdown().sizeAllCode;
for (int i = FRAGMENT_NUMBER_TOTAL_PROGRAM; i <= numSplitPoints + 1; i++) {
SizeBreakdown breakdown;
if (i == FRAGMENT_NUMBER_TOTAL_PROGRAM) {
continue;
} else if (i == numSplitPoints + 1) { // leftovers
continue;
} else if (i == FRAGMENT_NUMBER_INITIAL_DOWNLOAD) {
continue;
} else {
breakdown = globalInformation.splitPointCodeBreakdown(i);
}
String drillDownFileName = shellFileName(breakdown, getPermutationId());
String splitPointDescription = globalInformation.getSplitPointToLocation().get(
i);
int size = breakdown.sizeAllCode;
float perc;
perc = ((float) size / (float) maxSize) * 100;
outFile.println("<tr>");
outFile.println("<td>" + i + "</td>");
outFile.println("<td><a href=\"" + drillDownFileName + "\">"
+ splitPointDescription + "</a></td>");
outFile.println("<td>");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + perc
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println(size + " Bytes (" + formatNumber(perc) + "%)");
outFile.println("</td>");
outFile.println("</tr>");
}
}
outFile.println("</tbody>");
outFile.println("</table>");
outFile.println("</div>");
addStandardHtmlEnding(outFile);
outFile.close();
}
private void addLefttoversStatus(String className, String packageName,
PrintWriter outFile) {
outFile.println("<ul class=\"soyc-exclusive\">");
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>");
}
/**
* Returns a file name for the dependencies list.
*/
private String dependenciesFileName(String depGraphName, String packageName) {
return "methodDependencies-" + depGraphName + "-" + filename(packageName)
+ "-" + getPermutationId() + ".html";
}
/**
* Format floating point number to two decimal points.
*
* @param number
* @return formatted number
*/
private String formatNumber(float number) {
DecimalFormat formatBy = new DecimalFormat("#.##");
return formatBy.format(number);
}
/**
* Return a {@link File} object for a file to be emitted into the output
* directory.
*/
private OutputStream getOutFile(String localFileName) throws IOException {
return outDir.getOutputStream(localFileName);
}
/**
* @return the ID for the current permutation
*/
private String getPermutationId() {
return globalInformation.getPermutationId();
}
/**
* 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);
}
/**
* Returns whether a split point is initial or not.
*
* @param splitPoint
* @returns true of the split point is initial, false otherwise
*/
private boolean isInitialSplitPoint(int splitPoint) {
return globalInformation.getSplitPointInitialLoadSequence().contains(
splitPoint);
}
/**
* Makes a file name for a leftovers status file.
*
* @param className
* @return the file name of the leftovers status file
*/
private String leftoversStatusFileName(String className) {
return "leftoverStatus-" + filename(className) + "-" + getPermutationId()
+ ".html";
}
/**
* Produces an HTML file that breaks down by code type.
*
* @param breakdown
* @param nameToCodeColl
* @param nameToLitColl
* @return the name of the produced file
* @throws IOException
*/
private String makeCodeTypeHtml(SizeBreakdown breakdown,
Map<String, CodeCollection> nameToCodeColl,
Map<String, LiteralsCollection> nameToLitColl) throws IOException {
String outFileName = breakdown.getId() + "-" + getPermutationId()
+ "-codeTypeBreakdown.html";
int sumSize = 0;
TreeMap<Integer, Set<String> > sortedCodeTypes = new TreeMap<Integer, Set<String> >(
Collections.reverseOrder());
for (String codeType : nameToCodeColl.keySet()) {
int curSize = nameToCodeColl.get(codeType).getCumSize(breakdown);
sumSize += curSize;
if (curSize != 0) {
if (sortedCodeTypes.containsKey(curSize)) {
Set<String> existingSet = sortedCodeTypes.get(curSize);
existingSet.add(codeType);
sortedCodeTypes.put(curSize, existingSet);
} else {
Set<String> newSet = new TreeSet<String>();
newSet.add(codeType);
sortedCodeTypes.put(curSize, newSet);
}
}
}
final PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
addSmallHtmlProlog(outFile, "Code breakdown");
outFile.println("<body class=\"soyc-breakdown\">");
outFile.println("<div class=\"g-doc\">");
outFile.println("<table class=\"soyc-table\">");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-splitpoint-type-col\">");
outFile.println("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>Type</th>");
outFile.println("<th>Size <span class=\"soyc-th-units\">(Bytes)</span></th>");
outFile.println("</thead>");
for (Integer size : sortedCodeTypes.keySet()) {
Set<String> codeTypes = sortedCodeTypes.get(size);
for (String codeType : codeTypes) {
String drillDownFileName = breakdown.getId() + "_" + codeType + "-"
+ getPermutationId() + "Classes.html";
float perc = ((float) size / (float) sumSize) * 100;
outFile.println("<tr>");
outFile.println("<td><a href=\"" + drillDownFileName
+ "\" target=\"_top\">" + codeType + "</a></td>");
outFile.println("<td>");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + perc
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println(size + " (" + formatNumber(perc) + "%)");
outFile.println("</td>");
outFile.println("</tr>");
}
}
outFile.println("</table>");
int stringSize = nameToLitColl.get("string").size;
String drillDownFileName = breakdown.getId() + "_string-"
+ getPermutationId() + "Lits.html";
outFile.println("<p class=\"soyc-breakdown-strings\">" + stringSize + " bytes occupied by <a href=\""
+ drillDownFileName + "\" target=\"_top\">Strings</a></p>");
int unaccountedForSize = breakdown.sizeAllCode - sumSize - stringSize;
outFile.println("<p class=\"soyc-breakdown-strings\">" + unaccountedForSize + " bytes of the JavaScript output cannot be attributed to any package or code type.</p>");
addStandardHtmlEnding(outFile);
outFile.close();
return outFileName;
}
/**
* Produces an HTML file that displays dependencies.
*
* @param depGraphName name of dependency graph
* @param dependencies map of dependencies
* @throws IOException
*/
private void makeDependenciesHtml(String depGraphName,
Map<String, String> dependencies) throws IOException {
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);
outFile = new PrintWriter(getOutFile(outFileName));
String packageDescription = packageName.length() == 0
? "the default package" : packageName;
addStandardHtmlProlog(outFile, "Method Dependencies for "
+ depGraphDescription, "Method Dependencies for "
+ depGraphDescription, "Showing Package: " + packageDescription);
}
String name = method;
if (curClassName.compareTo(className) != 0) {
name = className;
curClassName = className;
outFile.println("<a name=\"" + curClassName
+ "\"><h3 class=\"soyc-class-header\">Class: " + curClassName
+ "</a></h3>");
}
outFile.println("<div class='main'>");
outFile.println("<a class='toggle soyc-call-stack-link' onclick='toggle.call(this)'><span class='calledBy'> Call stack: </span>"
+ name + "</a>");
outFile.println("<ul class=\"soyc-call-stack-list\">");
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();
}
/**
* Produces an HTML file for leftovers status.
*
* @param className
* @throws IOException
*/
private void makeLeftoversStatusPage(String className) throws IOException {
String packageName = globalInformation.getClassToPackage().get(className);
PrintWriter outFile = new PrintWriter(
getOutFile(leftoversStatusFileName(className)));
addStandardHtmlProlog(outFile, "Leftovers page for " + className,
"Leftovers page for " + className, "");
outFile.println("<div id=\"bd\">");
outFile.println("<p>This class has some leftover code, neither initial nor exclusive to any split point:</p>");
addLefttoversStatus(className, packageName, outFile);
addStandardHtmlEnding(outFile);
outFile.close();
}
/**
* Produces an HTML file that shows information about a package.
*
* @param breakdown
* @return the name of the HTML file
* @throws IOException
*/
private String makePackageHtml(SizeBreakdown breakdown) throws IOException {
String outFileName = breakdown.getId() + "-" + getPermutationId() + "-"
+ "packageBreakdown.html";
Map<String, Integer> packageToPartialSize = breakdown.packageToSize;
TreeMap<Integer, Set<String>> sortedPackages = new TreeMap<Integer, Set<String>>(
Collections.reverseOrder());
float sumSize = 0f;
for (String packageName : packageToPartialSize.keySet()) {
Integer curSize = packageToPartialSize.get(packageName);
if (sortedPackages.containsKey(curSize)) {
Set<String> existingSet = sortedPackages.get(curSize);
existingSet.add(packageName);
sortedPackages.put(curSize, existingSet);
} else {
Set<String> newSet = new TreeSet<String>();
newSet.add(packageName);
sortedPackages.put(curSize, newSet);
}
sumSize += curSize;
}
final PrintWriter outFile = new PrintWriter(getOutFile(outFileName));
addSmallHtmlProlog(outFile, "Package breakdown");
outFile.println("<body class=\"soyc-breakdown\">");
outFile.println("<div class=\"g-doc\">");
outFile.println("<table class=\"soyc-table\">");
outFile.println("<colgroup>");
outFile.println("<col id=\"soyc-splitpoint-type-col\">");
outFile.println("<col id=\"soyc-splitpoint-size-col\">");
outFile.println("</colgroup>");
outFile.println("<thead>");
outFile.println("<th>Package</th>");
outFile.println("<th>Size <span class=\"soyc-th-units\">(Bytes)</span></th>");
outFile.println("</thead>");
for (int size : sortedPackages.keySet()) {
Set<String> packageNames = sortedPackages.get(size);
for (String packageName : packageNames) {
String drillDownFileName = classesInPackageFileName(breakdown,
packageName, getPermutationId());
float perc = (size / sumSize) * 100;
outFile.println("<tr>");
outFile.println("<td><a href=\"" + drillDownFileName
+ "\" target=\"_top\">" + packageName + "</a></td>");
outFile.println("<td>");
outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">");
// CHECKSTYLE_OFF
outFile.println("<div style=\"width:" + perc
+ "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>");
// CHECKSTYLE_ON
outFile.println("</div>");
outFile.println(size + " (" + formatNumber(perc) + "%)");
outFile.println("</td>");
outFile.println("</tr>");
}
}
outFile.println("</table>");
addStandardHtmlEnding(outFile);
outFile.close();
return outFileName;
}
private void makeSplitStatusPage(String className) throws IOException {
String packageName = globalInformation.getClassToPackage().get(className);
PrintWriter outFile = new PrintWriter(
getOutFile(splitStatusFileName(className)));
addStandardHtmlProlog(outFile, "Split point status for " + className,
"Split point status for " + className, "");
outFile.println("<div id=\"bd\">");
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>");
} else {
outFile.println("<p>Some code is included in the initial fragment</p>");
}
}
for (int sp : splitPointsWithClass(className)) {
outFile.println("<p>Some code downloads with split point " + sp + ": "
+ globalInformation.getSplitPointToLocation().get(sp) + "</p>");
}
if (globalInformation.getLeftoversBreakdown().classToSize.containsKey(className)) {
outFile.println("<p>Some code is left over:</p>");
addLefttoversStatus(className, packageName, outFile);
}
addStandardHtmlEnding(outFile);
outFile.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) {
return "splitStatus-" + filename(className) + "-" + getPermutationId()
+ ".html";
}
}