| /* |
| * 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.linker.CompilationMetricsArtifact; |
| import com.google.gwt.core.ext.linker.ModuleMetricsArtifact; |
| import com.google.gwt.core.ext.linker.PrecompilationMetricsArtifact; |
| import com.google.gwt.core.ext.soyc.impl.SizeMapRecorder; |
| import com.google.gwt.dev.util.Util; |
| import com.google.gwt.soyc.io.OutputDirectory; |
| import com.google.gwt.thirdparty.guava.common.base.Joiner; |
| import com.google.gwt.thirdparty.guava.common.collect.Lists; |
| import com.google.gwt.thirdparty.guava.common.collect.Sets; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.PrintWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.text.NumberFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| 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 { |
| @Override |
| public String dependencyLinkForClass(String className) { |
| String packageName = globalInformation.getClassToPackage().get(className); |
| assert packageName != null; |
| return dependenciesFileName("initial") + "#" + className; |
| } |
| } |
| |
| /** |
| * A dependency linker for the leftovers fragment. It links to leftovers |
| * status pages. |
| */ |
| public class DependencyLinkerForLeftoversFragment implements DependencyLinker { |
| @Override |
| public String dependencyLinkForClass(String className) { |
| return leftoversStatusFileName() + "#" |
| + hashedFilenameFragment(className); |
| } |
| } |
| |
| /** |
| * A dependency linker for the total program breakdown. It links to a split |
| * status page. |
| * |
| */ |
| public class DependencyLinkerForTotalBreakdown implements DependencyLinker { |
| @Override |
| public String dependencyLinkForClass(String className) { |
| return splitStatusFileName() + "#" + hashedFilenameFragment(className); |
| } |
| } |
| |
| /** |
| * A dependency linker that never links to anything. |
| */ |
| public static class NullDependencyLinker implements DependencyLinker { |
| @Override |
| public String dependencyLinkForClass(String className) { |
| return null; |
| } |
| } |
| |
| interface DependencyLinker { |
| String dependencyLinkForClass(String className); |
| } |
| |
| /** |
| * 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 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; |
| |
| /** |
| * 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 = 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 (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 = methodDependencyString(method); |
| outFile.print("b(" + nameArray + ","); |
| outFile.print("["); |
| Set<String> visited = Sets.newHashSet(); |
| String separator = ""; |
| while (depMethod != null) { |
| String nextDep = dependencies.get(depMethod); |
| // The bottom of the stack frame is not interesting. |
| if (nextDep != null) { |
| if (!visited.add(nextDep)) { |
| break; |
| } |
| outFile.print(separator); |
| outFile.print(methodDependencyString(depMethod)); |
| separator = ","; |
| } |
| depMethod = nextDep; |
| } |
| outFile.print("]);"); |
| } |
| |
| private String methodDependencyString(String method) { |
| return "[" + frozen.get(getPackageSubstring(method)) + "," |
| + frozen.get(getClassSubstring(method)) + "," |
| + frozen.get(getMethodSubstring(method)) + "]"; |
| } |
| |
| /** |
| * 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 fragment = 1; fragment <= globalInformation.getNumFragments(); fragment++) { |
| final List<String> fragmentDescriptors = globalInformation.getFragmentDescriptors(fragment); |
| String[] escapedFragmentDescriptors = |
| new String[fragmentDescriptors.size()]; |
| for (int i = 0; i < fragmentDescriptors.size(); i++) { |
| escapedFragmentDescriptors[i] = |
| escapeJSString(fragmentDescriptors.get(i)); |
| } |
| outFile.println(" '" + Joiner.on(",").join(escapedFragmentDescriptors) + "',"); |
| } |
| outFile.println(" ];"); |
| |
| // object/dictionary containing method sizes |
| outFile.println(" var methodSizes = {"); |
| for (SizeBreakdown breakdown : globalInformation.allSizeBreakdowns()) { |
| for (Entry<String, Integer> methodEntry : breakdown.methodToSize.entrySet()) { |
| String methodSignature = methodEntry.getKey(); |
| String method = methodSignature.substring(0, methodSignature.indexOf("(")); |
| outFile.println(" \"" + method + "\" : " + methodEntry.getValue() + ","); |
| } |
| } |
| outFile.println("};"); |
| |
| // dropdown button image srcs |
| outFile.println(" var images = {"); |
| outFile.println(" \"closed\" : \"images/play-g16.png\","); |
| outFile.println(" \"open\" : \"images/play-g16-down.png\","); |
| 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(\"<div class='main'>\");"); |
| outFile.println(" document.write(\"<table class='soyc-table'>\");"); |
| outFile.println(" document.write(\"<thead>\");"); |
| outFile.println(" document.write(\"<th><a class='soyc-class-name' " |
| + "name='\" + className + \"'>" |
| + "Class: \" + className + \"</a></th>\");"); |
| outFile.println(" document.write(\"<th class='soyc-numerical-col-header'>Size " |
| + "<span class='soyc-th-units'>(bytes)</span></th>\");"); |
| outFile.println(" document.write(\"</thead>\");"); |
| outFile.println(" }"); |
| |
| outFile.println(" function swapShowHide(elementName) {"); |
| outFile.println(" hp = document.getElementById(elementName);"); |
| outFile.println(" arrow = document.getElementById(\"dropdown-\" + elementName);"); |
| outFile.println(" if (hp.style.display !== \"none\" && hp.style.display " |
| + "!== \"inline\") {"); |
| outFile.println(" hp.style.display = \"inline\";"); |
| outFile.println(" arrow.src = images[\"open\"];"); |
| outFile.println(" } else if (hp.style.display === \"none\") {"); |
| outFile.println(" hp.style.display = \"inline\";"); |
| outFile.println(" arrow.src = images[\"open\"];"); |
| outFile.println(" } else {"); |
| outFile.println(" hp.style.display = \"none\";"); |
| outFile.println(" arrow.src = images[\"closed\"];"); |
| outFile.println(" }"); |
| outFile.println(" }"); |
| |
| // function to print a single dependency in the methodDependencies report |
| // see printDependency() |
| outFile.println(" function b(c, deps) {"); |
| outFile.println(" var methodName = internedStrings[c[0]] + \".\" + internedStrings[c[1]] " |
| + "+ \"::\" + internedStrings[c[2]];"); |
| outFile.println(" var methodSize = methodSizes[methodName];"); |
| outFile.println(" if (methodSize === undefined) methodSize = \"--\";"); |
| outFile.println(" var callstackId = \"callstack-\" + methodName;"); |
| outFile.println(" document.write(\"<tr>\");"); |
| outFile.println(" document.write(\"<td>\");"); |
| outFile.println(" document.write(\"<img onclick='swapShowHide(\\\"\" + " |
| + "callstackId + \"\\\")'" |
| + "id='dropdown-\" + callstackId + \"' " |
| + "class='dropdown-img' " |
| + "src=\" + images[\"closed\"] + \">\");"); |
| outFile.println(" document.write(\"<a class='toggle soyc-call-stack-link' " |
| + "onclick='swapShowHide(\\\"\" + callstackId + \"\\\")'>\" + methodName + \"</a>\");"); |
| outFile.println(" document.write(\"<ul id='\" + callstackId + \"' " |
| + "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>\");"); |
| outFile.println(" document.write(\"</td>\");"); |
| outFile.println(" document.write(\"<td class='soyc-numerical-col'>\" + " |
| + "methodSize + \"</td>\");"); |
| outFile.println(" document.write(\"</tr>\");"); |
| outFile.println(" }"); |
| |
| // follows all method dependency stacks |
| outFile.println(" function j() {"); |
| outFile.println(" document.write(\"</table></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-" + 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.getNumFragments() + "; sp++) {"); |
| outFile.println(" var d2 = 'methodDependencies-sp' + sp + '-" + 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) + ");"); |
| } |
| } |
| |
| /** |
| * 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]+)"); |
| |
| 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>Permutation " + permutationId); |
| |
| for (String desc : permutationInfoList) { |
| outFile.println(" (" + desc + ")"); |
| } |
| outFile.println("<ul>"); |
| outFile.println("<li>"); |
| outFile.println("<a href=\"SoycDashboard-" + permutationId |
| + "-index.html\">Split Point Report</a>"); |
| outFile.println("</li>"); |
| outFile.println("<li>"); |
| outFile.println("<a href=\"CompilerMetrics-" + permutationId |
| + "-index.html\">Compiler Metrics</a>"); |
| outFile.println("</li>"); |
| |
| outFile.println("</ul>"); |
| outFile.println("</li>"); |
| } |
| outFile.println("</ul>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| /** |
| * @return given "com.foo.myClass" or "com.foo.myClass::myMethod" returns |
| * "myClass" |
| */ |
| static String getClassSubstring(String fullMethodName) { |
| if (fullMethodName.length() == 0) { |
| return ""; |
| } |
| int startIndex = getPackageSubstring(fullMethodName).length() + 1; |
| int endIndex = fullMethodName.indexOf("::"); |
| if (endIndex == -1) { |
| endIndex = fullMethodName.length(); |
| } |
| if (startIndex > endIndex || startIndex > fullMethodName.length()) { |
| return ""; |
| } |
| return fullMethodName.substring(startIndex, endIndex); |
| } |
| |
| /** |
| * @return given "com.foo.myClass::myMethod" returns "myMethod" |
| */ |
| static String getMethodSubstring(String fullMethodName) { |
| int index = fullMethodName.indexOf("::"); |
| if (index == -1) { |
| return ""; |
| } |
| index += 2; |
| if (index >= fullMethodName.length()) { |
| return ""; |
| } |
| return fullMethodName.substring(index); |
| } |
| |
| /** |
| * @return given "com.foo.myClass" or "com.foo.myClass::myMethod" returns |
| * "com.foo" |
| */ |
| static String getPackageSubstring(String fullMethodName) { |
| int endIndex = fullMethodName.lastIndexOf('.'); |
| if (endIndex == -1) { |
| endIndex = fullMethodName.length(); |
| } |
| return fullMethodName.substring(0, endIndex); |
| } |
| |
| 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\">"); |
| outFile.println("<span>Google Web Toolkit</span>"); |
| outFile.println("</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\">"); |
| if (header2 != null && header2.length() > 0) { |
| outFile.println("<h2>" + header2 + "</h2>"); |
| } |
| } |
| |
| private static String classesInPackageFileName(SizeBreakdown breakdown, |
| String permutationId) { |
| return breakdown.getId() + "_" + permutationId + "_Classes.html"; |
| } |
| |
| private String escapeJSString(String str) { |
| // TODO(rluble): make into a Util routine. |
| return str.replaceAll("/", "\\/"); |
| } |
| |
| private static String escapeXml(String unescaped) { |
| // TODO(rluble): see why SizeMapRecorder.escapeXML is different from Util.escapeXML. If two |
| // different versions are needed at all move SizeMapRecorder.escapeXML to Util. |
| return SizeMapRecorder.escapeXml(unescaped); |
| } |
| |
| /** |
| * Convert a potentially long string into a short file name. The current |
| * implementation simply hashes the long name. |
| */ |
| private static String hashedFilenameFragment(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", ""); |
| |
| 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."; |
| |
| outFile.println("<h2>"); |
| addPopupLink(outFile, popupName, popupTitle, null); |
| outFile.println("</h2></div>"); |
| addPopup(outFile, popupName, popupTitle, popupBody); |
| outFile.println("<iframe class='soyc-iframe-package' src=\"" |
| + packageBreakdownFileName + "\" scrolling=auto></iframe>"); |
| |
| popupName = "codeTypeBreakdownPopup"; |
| 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."; |
| outFile.println("<h2>"); |
| addPopupLink(outFile, popupName, popupTitle, null); |
| outFile.println("</h2>"); |
| addPopup(outFile, popupName, popupTitle, popupBody); |
| |
| 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>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">"); |
| outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>"); |
| outFile.println("</th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>"); |
| outFile.println("</thead>"); |
| |
| NumberFormat bytesFormatter = NumberFormat.getInstance(); |
| bytesFormatter.setGroupingUsed(true); |
| |
| NumberFormat percentFormatter = NumberFormat.getPercentInstance(); |
| percentFormatter.setMinimumFractionDigits(1); |
| percentFormatter.setMaximumFractionDigits(1); |
| |
| for (Integer size : sortedClasses.keySet()) { |
| Set<String> classNames = sortedClasses.get(size); |
| for (String className : classNames) { |
| float perc = (float) size / sumSize; |
| outFile.println("<tr>"); |
| outFile.println("<td>" + className + "</a></td>"); |
| outFile.println("<td class=\"soyc-bargraph-col\">"); |
| outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">"); |
| // CHECKSTYLE_OFF |
| outFile.println("<div style=\"width:" + (perc * 100.0) |
| + "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>"); |
| // CHECKSTYLE_ON |
| outFile.println("</div>"); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(bytesFormatter.format(size)); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-percent-col\">" + |
| percentFormatter.format(perc) + "</td>"); |
| outFile.println("</tr>"); |
| } |
| } |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| } |
| |
| public void makeCompilerMetricsPermFiles(ModuleMetricsArtifact moduleMetrics, |
| PrecompilationMetricsArtifact precompilationMetrics, |
| CompilationMetricsArtifact compilationMetrics) throws IOException { |
| String outFileName = "CompilerMetrics-" |
| + precompilationMetrics.getPermutationBase() + "-index.html"; |
| PrintWriter outFile = new PrintWriter(getOutFile(outFileName)); |
| String title = "Compiler Metrics for Permutation " |
| + compilationMetrics.getPermutationId(); |
| addStandardHtmlProlog(outFile, title, title, "Build Time Metrics"); |
| |
| NumberFormat elapsedFormatter = NumberFormat.getInstance(); |
| elapsedFormatter.setGroupingUsed(true); |
| elapsedFormatter.setMinimumFractionDigits(3); |
| elapsedFormatter.setMaximumFractionDigits(3); |
| |
| outFile.println("<div id=\"bd\">"); |
| int permutationId = compilationMetrics.getPermutationId(); |
| |
| // Build Time Metrics |
| outFile.println("<table class=\"soyc-table\">"); |
| outFile.println("<colgroup>"); |
| outFile.println("<col id=\"soyc-buildTimePhase-col\">"); |
| outFile.println("<col id=\"soyc-buildTimeElapsed-col\">"); |
| outFile.println("</colgroup>"); |
| outFile.println("<thead>"); |
| outFile.println("<th>Phase</th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">Elapsed Time</th>"); |
| outFile.println("</thead>"); |
| |
| outFile.println("<tr>"); |
| outFile.println("<td>"); |
| outFile.println("Module Analysis"); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">" |
| + elapsedFormatter.format(moduleMetrics.getElapsedMilliseconds() / 1000.0) + " s"); |
| outFile.println("</td>"); |
| outFile.println("</tr>"); |
| |
| outFile.println("<tr>"); |
| outFile.println("<td>"); |
| outFile.println("Precompile (may include Module Analysis)"); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">" |
| + elapsedFormatter.format(precompilationMetrics.getElapsedMilliseconds() / 1000.0) |
| + " s"); |
| outFile.println("</td>"); |
| outFile.println("</tr>"); |
| |
| outFile.println("<tr>"); |
| outFile.println("<td>"); |
| outFile.println("Compile"); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">" |
| + elapsedFormatter.format(compilationMetrics.getElapsedMilliseconds() / 1000.0) |
| + " s"); |
| outFile.println("</td>"); |
| outFile.println("</tr>"); |
| outFile.println("</table>"); |
| |
| NumberFormat referencesFormatter = NumberFormat.getInstance(); |
| referencesFormatter.setGroupingUsed(true); |
| |
| outFile.println("<p></p>"); |
| outFile.println("<h2>Source/Type Metrics</h2>"); |
| |
| outFile.println("<table class=\"soyc-table\">"); |
| outFile.println("<colgroup>"); |
| outFile.println("<col id=\"soyc-typeList-col\">"); |
| outFile.println("<col id=\"soyc-typeReferences-col\">"); |
| outFile.println("</colgroup>"); |
| outFile.println("<thead>"); |
| outFile.println("<th>Description</th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">References</th>"); |
| outFile.println("</thead>"); |
| |
| String sourcesFileName = "CompilerMetrics-sources.html"; |
| outFile.println("<tr>"); |
| outFile.println("<td>"); |
| String popupName = "compilerMetricsSourceFiles"; |
| String popupTitle = "Source files"; |
| String popupBody = "All source files on the module source path."; |
| addPopupLink(outFile, popupName, popupTitle, sourcesFileName); |
| addPopup(outFile, popupName, popupTitle, popupBody); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(referencesFormatter.format(moduleMetrics.getSourceFiles().length)); |
| outFile.println("</td>"); |
| outFile.println("</tr>"); |
| makeCompilerMetricsSources(sourcesFileName, moduleMetrics, popupBody); |
| |
| String initialTypesFileName = "CompilerMetrics-initialTypes-" |
| + permutationId + ".html"; |
| outFile.println("<tr>"); |
| outFile.println("<td>"); |
| popupName = "compilerMetricsInitialTypes"; |
| popupTitle = "Initial Type Oracle Types"; |
| popupBody = "All types in the type oracle after compiling sources on the source path."; |
| addPopupLink(outFile, popupName, popupTitle, initialTypesFileName); |
| addPopup(outFile, popupName, popupTitle, popupBody); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(referencesFormatter.format(moduleMetrics.getInitialTypes().length)); |
| outFile.println("</td>"); |
| outFile.println("</tr>"); |
| makeCompilerMetricsInitialTypeOracleTypes(initialTypesFileName, |
| moduleMetrics, popupBody); |
| |
| String finalTypesFileName = "CompilerMetrics-finalTypes-" + permutationId |
| + ".html"; |
| outFile.println("<tr>"); |
| outFile.println("<td>"); |
| popupName = "compilerMetricsFinalTypes"; |
| popupTitle = "Final Type Oracle Types"; |
| popupBody = "All types in the type oracle after constructing the Java AST."; |
| addPopupLink(outFile, popupName, popupTitle, finalTypesFileName); |
| addPopup(outFile, popupName, popupTitle, popupBody); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(referencesFormatter.format( |
| precompilationMetrics.getFinalTypeOracleTypes().length)); |
| outFile.println("</td>"); |
| outFile.println("</tr>"); |
| makeCompilerMetricsFinalTypeOracleTypes(finalTypesFileName, |
| precompilationMetrics, popupBody); |
| |
| String[] generatedTypes = getGeneratedTypes(moduleMetrics, |
| precompilationMetrics); |
| String generatedTypesFileName = "CompilerMetrics-generatedTypes-" |
| + permutationId + ".html"; |
| outFile.println("<tr>"); |
| outFile.println("<td>"); |
| popupName = "compilerMetricsGeneratedTypes"; |
| popupTitle = "GeneratedTypes"; |
| popupBody = "Types that were added to the type oracle while running generators."; |
| addPopupLink(outFile, popupName, popupTitle, generatedTypesFileName); |
| addPopup(outFile, popupName, popupTitle, popupBody); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(referencesFormatter.format(generatedTypes.length)); |
| outFile.println("</td>"); |
| outFile.println("</tr>"); |
| makeCompilerMetricsGeneratedTypes(generatedTypesFileName, generatedTypes, |
| popupBody); |
| |
| String astFileName = "CompilerMetrics-ast-" + permutationId + ".html"; |
| outFile.println("<tr>"); |
| outFile.println("<td>"); |
| popupName = "compilerMetricsAstTypes"; |
| popupTitle = "AST Referenced Types"; |
| popupBody = "All types referenced by the Java AST after performing " |
| + "reachability analysis from the module EntryPoint."; |
| addPopupLink(outFile, popupName, popupTitle, astFileName); |
| addPopup(outFile, popupName, popupTitle, popupBody); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(referencesFormatter.format(precompilationMetrics.getAstTypes().length)); |
| outFile.println("</td>"); |
| outFile.println("</tr>"); |
| makeCompilerMetricsAstTypes(astFileName, precompilationMetrics, popupBody); |
| |
| String[] unreferencedTypes = getUnreferencedTypes(precompilationMetrics); |
| String unreferencedFileName = "CompilerMetrics-unreferencedTypes-" |
| + permutationId + ".html"; |
| outFile.println("<tr>"); |
| outFile.println("<td>"); |
| popupName = "compilerMetricsUnreferenceTypes"; |
| popupTitle = "Unreferenced Types"; |
| popupBody = "Types that were on the initial source path but never referenced in " |
| + "the Java AST."; |
| addPopupLink(outFile, popupName, popupTitle, unreferencedFileName); |
| addPopup(outFile, popupName, popupTitle, popupBody); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(referencesFormatter.format(unreferencedTypes.length)); |
| outFile.println("</td>"); |
| outFile.println("</tr>"); |
| makeCompilerMetricsUnreferencedTypes(unreferencedFileName, |
| unreferencedTypes, popupBody); |
| outFile.println("</table>"); |
| |
| 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 { |
| PrintWriter outFile = new PrintWriter(getOutFile(leftoversStatusFileName())); |
| addStandardHtmlProlog(outFile, "Leftovers page", "Leftovers page", ""); |
| outFile.println("<div id=\"bd\">"); |
| 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; |
| interner.printPackageHeader(outFile, packageName); |
| } |
| interner.printClassHeader(outFile, className); |
| if (globalInformation.dependencies != null) { |
| interner.printLeftoversStatus(outFile, packageName, className); |
| } |
| } |
| outFile.println("</script>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| 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 { |
| 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]); |
| Arrays.sort(packageNames); |
| for (String packageName : packageNames) { |
| 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; |
| } |
| } |
| |
| if (sortedClasses.size() > 0) { |
| outFile.println("<p>"); |
| outFile.println("<table class=\"soyc-table\">"); |
| outFile.print("<colgroup>"); |
| outFile.print("<col id=\"soyc-splitpoint-type-col\">"); |
| outFile.print("<col id=\"soyc-splitpoint-size-col\">"); |
| outFile.println("</colgroup>"); |
| outFile.print("<thead>"); |
| outFile.print("<a name=\"" + hashedFilenameFragment(packageName) |
| + "\"></a><th>Package: " + packageName + "</th>"); |
| outFile.println("<th></th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">"); |
| outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>"); |
| outFile.println("</th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>"); |
| outFile.print("</thead>"); |
| |
| NumberFormat bytesFormatter = NumberFormat.getInstance(); |
| bytesFormatter.setGroupingUsed(true); |
| |
| NumberFormat percentFormatter = NumberFormat.getPercentInstance(); |
| percentFormatter.setMinimumFractionDigits(1); |
| percentFormatter.setMaximumFractionDigits(1); |
| |
| 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; |
| 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 class=\"soyc-bargraph-col\">"); |
| outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">"); |
| // CHECKSTYLE_OFF |
| outFile.println("<div style=\"width:" + (perc * 100.0) |
| + "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>"); |
| // CHECKSTYLE_ON |
| outFile.println("</div>"); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(bytesFormatter.format(size)); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-percent-col\">" + |
| percentFormatter.format(perc) + "</td>"); |
| outFile.println("</tr>"); |
| } |
| } |
| outFile.println("</table>"); |
| outFile.println("</p>"); |
| } |
| } |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| public void makeSplitStatusPages() throws IOException { |
| PrintWriter outFile = new PrintWriter(getOutFile(splitStatusFileName())); |
| |
| addStandardHtmlProlog(outFile, "Split point status", "Split point status", |
| ""); |
| outFile.println("<div id=\"bd\">"); |
| |
| 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; |
| interner.printPackageHeader(outFile, packageName); |
| } |
| |
| interner.printClassHeader(outFile, className); |
| |
| if (globalInformation.getInitialCodeBreakdown().classToSize.containsKey(className)) { |
| if (globalInformation.dependencies != null) { |
| interner.printHasInitialFragment(outFile, className); |
| } else { |
| interner.printHasInitialFragment(outFile); |
| } |
| } |
| for (int sp : splitPointsWithClass(className)) { |
| interner.printHasCodeInSplitPoint(outFile, className, sp); |
| } |
| if (globalInformation.getLeftoversBreakdown().classToSize.containsKey(className)) { |
| interner.printSomeCodeLeftover(outFile); |
| interner.printLeftoversStatus(outFile, packageName, className); |
| } |
| } |
| outFile.println("</script>"); |
| |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| 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, ""); |
| |
| NumberFormat bytesFormatter = NumberFormat.getInstance(); |
| bytesFormatter.setGroupingUsed(true); |
| |
| 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\">" |
| + bytesFormatter.format(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\">" |
| + bytesFormatter.format(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\">" |
| + bytesFormatter.format(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></th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">"); |
| outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>"); |
| outFile.println("</th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>"); |
| outFile.println("</thead>"); |
| outFile.println("<tbody>"); |
| |
| NumberFormat percentFormatter = NumberFormat.getPercentInstance(); |
| percentFormatter.setMinimumFractionDigits(1); |
| percentFormatter.setMaximumFractionDigits(1); |
| |
| if (globalInformation.getNumFragments() >= 1) { |
| |
| int numFragments = globalInformation.getNumFragments(); |
| int maxSize = globalInformation.getTotalCodeBreakdown().sizeAllCode; |
| |
| for (int fragment = FRAGMENT_NUMBER_TOTAL_PROGRAM; fragment <= numFragments + 1; fragment++) { |
| SizeBreakdown breakdown; |
| if (fragment == FRAGMENT_NUMBER_TOTAL_PROGRAM |
| || fragment == numFragments + 1 // leftovers |
| || fragment == FRAGMENT_NUMBER_INITIAL_DOWNLOAD) { |
| continue; |
| } |
| |
| breakdown = globalInformation.fragmentCodeBreakdown(fragment); |
| |
| String drillDownFileName = shellFileName(breakdown, getPermutationId()); |
| final List<String> fragmentDescriptors = globalInformation.getFragmentDescriptors(fragment); |
| String[] escapedFragmentDescriptors = |
| new String[fragmentDescriptors.size()]; |
| for (int i = 0; i < fragmentDescriptors.size(); i++) { |
| escapedFragmentDescriptors[i] = |
| escapeXml(fragmentDescriptors.get(i)); |
| } |
| |
| String fragmentDescription = Joiner.on("<BR>").join(escapedFragmentDescriptors); |
| |
| int size = breakdown.sizeAllCode; |
| float perc = (float) size / (float) maxSize; |
| |
| outFile.println("<tr>"); |
| outFile.println("<td>" + fragment + "</td>"); |
| outFile.print("<td><a href=\"" + drillDownFileName + "\">" + fragmentDescription |
| + "</a></td>"); |
| outFile.println("<td class=\"soyc-bargraph-col\">"); |
| outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">"); |
| // CHECKSTYLE_OFF |
| outFile.println("<div style=\"width:" + (perc * 100.0) |
| + "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>"); |
| // CHECKSTYLE_ON |
| outFile.println("</div>"); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(bytesFormatter.format(size)); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-percent-col\">" + |
| percentFormatter.format(perc) + "</td>"); |
| outFile.println("</tr>"); |
| } |
| } |
| outFile.println("</tbody>"); |
| outFile.println("</table>"); |
| outFile.println("</div>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| private void addPopup(PrintWriter outFile, String popupName, |
| String popupTitle, String popupBody) { |
| outFile.println("<div class=\"soyc-popup\" id=\"" + popupName + "\">"); |
| outFile.println("<table>"); |
| outFile.println("<tr><th><b>" + popupTitle + "</b></th></tr>"); |
| outFile.println("<tr><td>" + popupBody + "</td></tr>"); |
| outFile.println("</table>"); |
| outFile.println("</div>"); |
| } |
| |
| private void addPopupLink(PrintWriter outFile, String popupName, |
| String popupTitle, String href) { |
| outFile.println("<a "); |
| if (href != null) { |
| outFile.println("href=\"" + href + "\""); |
| } |
| outFile.println("style=\"cursor:default;\" onMouseOver=\"show('" |
| + popupName + "');\" " + "onMouseOut=\"hide('" + popupName + "');\">" |
| + popupTitle + "</a>"); |
| } |
| |
| /** |
| * Returns a file name for the dependencies list. |
| */ |
| private String dependenciesFileName(String depGraphName) { |
| return "methodDependencies-" + depGraphName + "-" + getPermutationId() + ".html"; |
| } |
| |
| private String[] getGeneratedTypes(ModuleMetricsArtifact moduleMetrics, |
| PrecompilationMetricsArtifact precompilationMetrics) { |
| List<String> initialTypes = Lists.newArrayList(moduleMetrics.getInitialTypes()); |
| Set<String> generatedTypes = Sets.newHashSet(precompilationMetrics.getFinalTypeOracleTypes()); |
| generatedTypes.removeAll(initialTypes); |
| String[] results = generatedTypes.toArray(new String[generatedTypes.size()]); |
| Arrays.sort(results); |
| return results; |
| } |
| |
| /** |
| * Return a {@link java.io.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(); |
| } |
| |
| private String[] getUnreferencedTypes( |
| PrecompilationMetricsArtifact precompilationMetrics) { |
| List<String> astTypes = Lists.newArrayList(precompilationMetrics.getAstTypes()); |
| Set<String> unreferencedTypes = Sets.newHashSet(precompilationMetrics.getFinalTypeOracleTypes()); |
| unreferencedTypes.removeAll(astTypes); |
| String[] results = unreferencedTypes.toArray(new String[unreferencedTypes.size()]); |
| Arrays.sort(results); |
| return results; |
| } |
| |
| /** |
| * 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.getInitialFragmentLoadSequence().contains( |
| splitPoint); |
| } |
| |
| /** |
| * Makes a file name for a leftovers status file. |
| * |
| * @return the file name of the leftovers status file |
| */ |
| private String leftoversStatusFileName() { |
| return "leftoverStatus-" + 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></th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">"); |
| outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>"); |
| outFile.println("</th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>"); |
| outFile.println("</thead>"); |
| |
| NumberFormat bytesFormatter = NumberFormat.getInstance(); |
| bytesFormatter.setGroupingUsed(true); |
| |
| NumberFormat percentFormatter = NumberFormat.getPercentInstance(); |
| percentFormatter.setMinimumFractionDigits(1); |
| percentFormatter.setMaximumFractionDigits(1); |
| |
| 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; |
| outFile.println("<tr>"); |
| outFile.println("<td><a href=\"" + drillDownFileName |
| + "\" target=\"_top\">" + codeType + "</a></td>"); |
| outFile.println("<td class=\"soyc-bargraph-col\">"); |
| outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">"); |
| // CHECKSTYLE_OFF |
| outFile.println("<div style=\"width:" + (perc * 100.0) |
| + "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>"); |
| // CHECKSTYLE_ON |
| outFile.println("</div>"); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(bytesFormatter.format(size)); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-percent-col\">" + |
| percentFormatter.format(perc) + "</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; |
| } |
| |
| private void makeCompilerMetricsAstTypes(String outFileName, |
| PrecompilationMetricsArtifact precompilationMetrics, String helpText) |
| throws IOException { |
| PrintWriter outFile = new PrintWriter(getOutFile(outFileName)); |
| String title = "AST Types"; |
| addStandardHtmlProlog(outFile, title, title, ""); |
| outFile.println("<p>"); |
| outFile.println(helpText); |
| outFile.println("</p>"); |
| outFile.println("<pre>"); |
| String[] types = precompilationMetrics.getAstTypes(); |
| Arrays.sort(types); |
| for (String type : types) { |
| outFile.println(type); |
| } |
| outFile.println("</pre>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| private void makeCompilerMetricsFinalTypeOracleTypes(String outFileName, |
| PrecompilationMetricsArtifact precompilationMetrics, String helpText) |
| throws IOException { |
| PrintWriter outFile = new PrintWriter(getOutFile(outFileName)); |
| String title = "Final Type Oracle Types"; |
| addStandardHtmlProlog(outFile, title, title, ""); |
| outFile.println("<p>"); |
| outFile.println(helpText); |
| outFile.println("</p>"); |
| outFile.println("<pre>"); |
| String[] types = precompilationMetrics.getFinalTypeOracleTypes(); |
| Arrays.sort(types); |
| for (String type : types) { |
| outFile.println(type); |
| } |
| outFile.println("</pre>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| private void makeCompilerMetricsGeneratedTypes(String outFileName, |
| String[] generatedTypes, String helpText) throws IOException { |
| PrintWriter outFile = new PrintWriter(getOutFile(outFileName)); |
| String title = "Generated Types"; |
| addStandardHtmlProlog(outFile, title, title, ""); |
| outFile.println("<p>"); |
| outFile.println(helpText); |
| outFile.println("</p>"); |
| outFile.println("<pre>"); |
| for (String type : generatedTypes) { |
| outFile.println(type); |
| } |
| outFile.println("</pre>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| private void makeCompilerMetricsInitialTypeOracleTypes(String outFileName, |
| ModuleMetricsArtifact moduleMetrics, String helpText) throws IOException { |
| PrintWriter outFile = new PrintWriter(getOutFile(outFileName)); |
| String title = "Initial Type Oracle Types (built from source path)"; |
| addStandardHtmlProlog(outFile, title, title, ""); |
| outFile.println("<p>"); |
| outFile.println(helpText); |
| outFile.println("</p>"); |
| outFile.println("<pre>"); |
| String[] types = moduleMetrics.getInitialTypes(); |
| Arrays.sort(types); |
| for (String type : types) { |
| outFile.println(type); |
| } |
| outFile.println("</pre>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| private void makeCompilerMetricsSources(String outFileName, |
| ModuleMetricsArtifact moduleMetrics, String helpText) throws IOException { |
| PrintWriter outFile = new PrintWriter(getOutFile(outFileName)); |
| String title = "Sources on Source Path"; |
| addStandardHtmlProlog(outFile, title, title, ""); |
| outFile.println("<p>"); |
| outFile.println(helpText); |
| outFile.println("</p>"); |
| outFile.println("<pre>"); |
| String[] sources = moduleMetrics.getSourceFiles(); |
| Arrays.sort(sources); |
| for (String source : sources) { |
| outFile.println(source); |
| } |
| outFile.println("</pre>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| private void makeCompilerMetricsUnreferencedTypes(String outFileName, |
| String[] unreferencedTypes, String helpText) throws IOException { |
| PrintWriter outFile = new PrintWriter(getOutFile(outFileName)); |
| String title = "Unreferenced Types"; |
| addStandardHtmlProlog(outFile, title, title, ""); |
| outFile.println("<p>"); |
| outFile.println(helpText); |
| outFile.println("</p>"); |
| outFile.println("<pre>"); |
| for (String type : unreferencedTypes) { |
| outFile.println(type); |
| } |
| outFile.println("</pre>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| private static void internMethod(HtmlInterner interner, String method) { |
| interner.intern(getPackageSubstring(method)); |
| interner.intern(getClassSubstring(method)); |
| interner.intern(getMethodSubstring(method)); |
| } |
| |
| /** |
| * 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 curPackageName = ""; |
| |
| HtmlInterner interner = new HtmlInterner(); |
| |
| for (Entry<String, String> dependencyEntry : dependencies.entrySet()) { |
| internMethod(interner, dependencyEntry.getKey()); |
| internMethod(interner, dependencyEntry.getValue()); |
| } |
| 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(); |
| |
| List<String> classesInPackage = new ArrayList<String>(); |
| for (String method : dependencies.keySet()) { |
| classesInPackage.add(method); |
| } |
| makeDependenciesInternedHtml(depGraphName, |
| 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, |
| List<String> classesInSplitPoint, |
| Map<String, String> dependencies, HtmlInterner interner, String jsFileName) |
| throws IOException { |
| String depGraphDescription = inferDepGraphDescription(depGraphName); |
| PrintWriter outFile = null; |
| String curClassName = ""; |
| |
| String outFileName = dependenciesFileName(depGraphName); |
| outFile = new PrintWriter(getOutFile(outFileName)); |
| |
| addStandardHtmlProlog(outFile, "Method Dependencies for " |
| + depGraphDescription, |
| "Method Dependencies for " + depGraphDescription, null); |
| |
| outFile.print("<script src=\"" + jsFileName |
| + "\" language=\"javascript\" ></script>"); |
| |
| // Write out the HTML |
| outFile.print("<script>"); |
| for (String method : classesInSplitPoint) { |
| // this key set is already in alphabetical order |
| // get the package of this method, i.e., everything up to .[A-Z] |
| |
| String className = method.replaceAll("::.*", ""); |
| String depMethod = dependencies.get(method); |
| if (curClassName.compareTo(className) != 0) { |
| curClassName = className; |
| if (method != classesInSplitPoint.get(0)) { |
| outFile.print("j();"); // close the previous table if not the first |
| } |
| interner.printDependencyClassHeader(outFile, className); |
| } |
| interner.printDependency(outFile, dependencies, method, depMethod); |
| } |
| outFile.println("</script>"); |
| |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| } |
| |
| /** |
| * Produces an HTML file that shows information about a package. |
| * |
| * @param breakdown |
| * @return the name of the HTML file |
| */ |
| 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>Packages (Sorted by size)</th>"); |
| outFile.println("<th></th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">"); |
| outFile.println("Size <span class=\"soyc-th-units\">(Bytes)</span>"); |
| outFile.println("</th>"); |
| outFile.println("<th class=\"soyc-numerical-col-header\">% of total</th>"); |
| outFile.println("</thead>"); |
| |
| NumberFormat bytesFormatter = NumberFormat.getInstance(); |
| bytesFormatter.setGroupingUsed(true); |
| |
| NumberFormat percentFormatter = NumberFormat.getPercentInstance(); |
| percentFormatter.setMinimumFractionDigits(1); |
| percentFormatter.setMaximumFractionDigits(1); |
| |
| for (int size : sortedPackages.keySet()) { |
| if (size == 0) { |
| continue; |
| } |
| Set<String> packageNames = sortedPackages.get(size); |
| for (String packageName : packageNames) { |
| String drillDownFileName = classesInPackageFileName(breakdown, |
| getPermutationId()) |
| + "#" + hashedFilenameFragment(packageName); |
| float perc = size / sumSize; |
| outFile.println("<tr>"); |
| outFile.println("<td><a href=\"" + drillDownFileName |
| + "\" target=\"_top\">" + packageName + "</a></td>"); |
| outFile.println("<td class=\"soyc-bargraph-col\">"); |
| outFile.println("<div class=\"soyc-bar-graph goog-inline-block\">"); |
| // CHECKSTYLE_OFF |
| outFile.println("<div style=\"width:" + (perc * 100.0) |
| + "%;\" class=\"soyc-bar-graph-fill goog-inline-block\"></div>"); |
| // CHECKSTYLE_ON |
| outFile.println("</div>"); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-numerical-col\">"); |
| outFile.println(bytesFormatter.format(size)); |
| outFile.println("</td>"); |
| outFile.println("<td class=\"soyc-percent-col\">" + |
| percentFormatter.format(perc) + "</td>"); |
| outFile.println("</tr>"); |
| } |
| } |
| |
| outFile.println("</table>"); |
| addStandardHtmlEnding(outFile); |
| outFile.close(); |
| return outFileName; |
| } |
| |
| /** |
| * 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.getNumFragments(); sp++) { |
| Map<String, Integer> classToSize = globalInformation.fragmentCodeBreakdown(sp).classToSize; |
| if (classToSize.containsKey(className)) { |
| sps.add(sp); |
| } |
| } |
| return sps; |
| } |
| |
| private String splitStatusFileName() { |
| return "splitStatus-" + getPermutationId() + ".html"; |
| } |
| } |