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