package jdiff; import java.util.*; import java.io.*; import java.text.*; /** * Emit an HTML file containing statistics about the differences. * Statistical information only appears if the -stats argument is used. * * See the file LICENSE.txt for copyright details. * @author Matthew Doar, mdoar@pobox.com */ public class HTMLStatistics { /** Constructor. */ public HTMLStatistics(HTMLReportGenerator h) { h_ = h; } /** The HTMLReportGenerator instance used to write HTML. */ private HTMLReportGenerator h_ = null; /** * Emit the statistics HTML file. */ public void emitStatistics(String filename, APIDiff apiDiff) { try { FileOutputStream fos = new FileOutputStream(filename); h_.reportFile = new PrintWriter(fos); // Write out the HTML header h_.writeStartHTMLHeader(); String oldAPIName = "Old API"; if (apiDiff.oldAPIName_ != null) oldAPIName = apiDiff.oldAPIName_; String newAPIName = "New API"; if (apiDiff.newAPIName_ != null) newAPIName = apiDiff.newAPIName_; // Write out the title h_.writeHTMLTitle("API Change Statistics"); h_.writeStyleSheetRef(); h_.writeText(""); h_.writeText(""); // writeText("
"); // Add the nav bar for the summary page // Write a customized navigation bar for the statistics page h_.writeText(""); SimpleDateFormat formatter = new SimpleDateFormat ("yyyy.MM.dd HH:mm"); Date day = new Date(); h_.writeText(""); h_.writeText("
"); h_.writeText("
"); h_.writeText("\"Android"); h_.writeText("
"); h_.writeText("
"); h_.writeText("
"); h_.writeText(""); h_.writeText(""); h_.writeText(""); h_.writeText("Android Developers | Android.com"); h_.writeText(""); h_.writeText("
"); h_.writeText("
"); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText("
API Diff Specification
To Level:" + newAPIName + "
From Level:" + oldAPIName + "
Generated" + formatter.format( day ) + "
"); h_.writeText("
"); h_.writeText("
"); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText("
Top of Report"); h_.writeText("
"); h_.writeText("
"); h_.writeText("
"); h_.writeText("
"); h_.writeText("
"); h_.writeText("
"); h_.writeText("
"); h_.writeText("

API Change Statistics

"); DecimalFormat df2 = new DecimalFormat( "#,###,###,##0.00" ); double dd = apiDiff.pdiff; double dd2dec = new Double(df2.format(dd)).doubleValue(); h_.writeText("

The overall difference between API Levels " + oldAPIName + " and " + newAPIName + " is approximately " + dd2dec + "%."); h_.writeText("

"); h_.writeText("
"); h_.writeText(""); h_.writeText("

Total of Differences, by Number and Type

"); h_.writeText("

"); h_.writeText("The table below lists the numbers of program elements (packages, classes, constructors, methods, and fields) that were added, changed, or removed. The table includes only the highest-level program elements — that is, if a class with two methods was added, the number of methods added does not include those two methods, but the number of classes added does include that class."); h_.writeText("

"); emitNumbersByElement(apiDiff); h_.writeText("
"); h_.writeText(""); h_.writeText("

Changed Packages, Sorted by Percentage Difference

"); emitPackagesByDiff(apiDiff); h_.writeText("

* See Calculation of Change Percentages, below.

"); h_.writeText("
"); h_.writeText(""); h_.writeText("

Changed Classes and Interfaces, Sorted by Percentage Difference

"); emitClassesByDiff(apiDiff); h_.writeText("

* See Calculation of Change Percentages, below.

"); h_.writeText("
"); h_.writeText("

Calculation of Change Percentages

"); h_.writeText("

"); h_.writeText("The percent change statistic reported for all elements in the "to" API Level specification is defined recursively as follows:

"); h_.writeText("
");
            h_.writeText("Percentage difference = 100 * (added + removed + 2*changed)");
            h_.writeText("                        -----------------------------------");
            h_.writeText("                        sum of public elements in BOTH APIs");
            h_.writeText("
"); h_.writeText("

where added is the number of packages added, removed is the number of packages removed, and changed is the number of packages changed."); h_.writeText("This definition is applied recursively for the classes and their program elements, so the value for a changed package will be less than 1, unless every class in that package has changed."); h_.writeText("The definition ensures that if all packages are removed and all new packages are"); h_.writeText("added, the change will be 100%.

"); h_.writeText("
"); h_.writeText("
"); h_.writeText("
"); h_.writeText(" Except as noted, this content is licensed under "); h_.writeText(" Creative Commons Attribution 2.5."); h_.writeText(" For details and restrictions, see the Content License."); h_.writeText("
"); h_.writeText("
"); h_.writeText("

"); h_.writeText(" Site Terms of Service -"); h_.writeText(" Privacy Policy -"); h_.writeText(" Brand Guidelines"); h_.writeText("

"); h_.writeText("
"); h_.writeText("
"); h_.writeText("
"); h_.writeText("
"); h_.writeText(""); h_.writeText(""); h_.writeText(""); h_.reportFile.close(); } catch(IOException e) { System.out.println("IO Error while attempting to create " + filename); System.out.println("Error: " + e.getMessage()); System.exit(1); } } /** * Emit all packages sorted by percentage difference, and a histogram * of the values. */ public void emitPackagesByDiff(APIDiff apiDiff) { Collections.sort(apiDiff.packagesChanged, new ComparePkgPdiffs()); // Write out the table start h_.writeText(""); h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(""); int[] hist = new int[101]; for (int i = 0; i < 101; i++) { hist[i] = 0; } Iterator iter = apiDiff.packagesChanged.iterator(); while (iter.hasNext()) { PackageDiff pkg = (PackageDiff)(iter.next()); int bucket = (int)(pkg.pdiff); hist[bucket]++; h_.writeText(""); if (bucket != 0) h_.writeText(" "); else h_.writeText(" "); h_.writeText(" "); h_.writeText(""); } h_.writeText("
Percentage Difference*Package
" + bucket + "<1" + pkg.name_ + "
"); /* Emit the histogram of the results h_.writeText("
"); h_.writeText("

"); h_.writeText(""); h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(""); double total = 0; for (int i = 0; i < 101; i++) { total += hist[i]; } for (int i = 0; i < 101; i++) { if (hist[i] != 0) { h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(""); } } // Repeat the data in a format which is easier for spreadsheets h_.writeText(""); h_.writeText("
Percentage
Difference
FrequencyPercentage Frequency
" + i + "" + (hist[i]/total) + "\"|\"
"); */ } /** * Emit all classes sorted by percentage difference, and a histogram * of the values.. */ public void emitClassesByDiff(APIDiff apiDiff) { // Add all the changed classes to a list List allChangedClasses = new ArrayList(); Iterator iter = apiDiff.packagesChanged.iterator(); while (iter.hasNext()) { PackageDiff pkg = (PackageDiff)(iter.next()); if (pkg.classesChanged != null) { // Add the package name to the class name List cc = new ArrayList(pkg.classesChanged); Iterator iter2 = cc.iterator(); while (iter2.hasNext()) { ClassDiff classDiff = (ClassDiff)(iter2.next()); classDiff.name_ = pkg.name_ + "." + classDiff.name_; } allChangedClasses.addAll(cc); } } Collections.sort(allChangedClasses, new CompareClassPdiffs()); // Write out the table start h_.writeText(""); h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(""); int[] hist = new int[101]; for (int i = 0; i < 101; i++) { hist[i] = 0; } iter = allChangedClasses.iterator(); while (iter.hasNext()) { ClassDiff classDiff = (ClassDiff)(iter.next()); int bucket = (int)(classDiff.pdiff); hist[bucket]++; h_.writeText(""); if (bucket != 0) h_.writeText(" "); else h_.writeText(" "); h_.writeText(" "); else h_.writeText(classDiff.name_ + ""); h_.writeText(""); } h_.writeText("
Percentage
Difference*
Class or Interface
" + bucket + "<1"); if (classDiff.isInterface_) h_.writeText("" + classDiff.name_ + "
"); /* Emit the histogram of the results h_.writeText("


"); h_.writeText("

"); h_.writeText(""); h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(""); double total = 0; for (int i = 0; i < 101; i++) { total += hist[i]; } for (int i = 0; i < 101; i++) { if (hist[i] != 0) { h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(""); } } // Repeat the data in a format which is easier for spreadsheets h_.writeText(""); h_.writeText("
Percentage
Difference
FrequencyPercentage Frequency
" + i + "" + (hist[i]/total) + "\"|\"
"); */ } /** * Emit a table of numbers of removals, additions and changes by * package, class, constructor, method and field. */ public void emitNumbersByElement(APIDiff apiDiff) { // Local variables to hold the values int numPackagesRemoved = apiDiff.packagesRemoved.size(); int numPackagesAdded = apiDiff.packagesAdded.size(); int numPackagesChanged = apiDiff.packagesChanged.size(); int numClassesRemoved = 0; int numClassesAdded = 0; int numClassesChanged = 0; int numCtorsRemoved = 0; int numCtorsAdded = 0; int numCtorsChanged = 0; int numMethodsRemoved = 0; int numMethodsAdded = 0; int numMethodsChanged = 0; int numFieldsRemoved = 0; int numFieldsAdded = 0; int numFieldsChanged = 0; int numRemoved = 0; int numAdded = 0; int numChanged = 0; // Calculate the values Iterator iter = apiDiff.packagesChanged.iterator(); while (iter.hasNext()) { PackageDiff pkg = (PackageDiff)(iter.next()); numClassesRemoved += pkg.classesRemoved.size(); numClassesAdded += pkg.classesAdded.size(); numClassesChanged += pkg.classesChanged.size(); Iterator iter2 = pkg.classesChanged.iterator(); while (iter2.hasNext()) { ClassDiff classDiff = (ClassDiff)(iter2.next()); numCtorsRemoved += classDiff.ctorsRemoved.size(); numCtorsAdded += classDiff.ctorsAdded.size(); numCtorsChanged += classDiff.ctorsChanged.size(); numMethodsRemoved += classDiff.methodsRemoved.size(); numMethodsAdded += classDiff.methodsAdded.size(); numMethodsChanged += classDiff.methodsChanged.size(); numFieldsRemoved += classDiff.fieldsRemoved.size(); numFieldsAdded += classDiff.fieldsAdded.size(); numFieldsChanged += classDiff.fieldsChanged.size(); } } // Write out the table h_.writeText(""); h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(""); h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); int numPackages = numPackagesRemoved + numPackagesAdded + numPackagesChanged; h_.writeText(" "); h_.writeText(""); numRemoved += numPackagesRemoved; numAdded += numPackagesAdded; numChanged += numPackagesChanged; h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); int numClasses = numClassesRemoved + numClassesAdded + numClassesChanged; h_.writeText(" "); h_.writeText(""); numRemoved += numClassesRemoved; numAdded += numClassesAdded; numChanged += numClassesChanged; h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); int numCtors = numCtorsRemoved + numCtorsAdded + numCtorsChanged; h_.writeText(" "); h_.writeText(""); numRemoved += numCtorsRemoved; numAdded += numCtorsAdded; numChanged += numCtorsChanged; h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); int numMethods = numMethodsRemoved + numMethodsAdded + numMethodsChanged; h_.writeText(" "); h_.writeText(""); numRemoved += numMethodsRemoved; numAdded += numMethodsAdded; numChanged += numMethodsChanged; h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); int numFields = numFieldsRemoved + numFieldsAdded + numFieldsChanged; h_.writeText(" "); h_.writeText(""); numRemoved += numFieldsRemoved; numAdded += numFieldsAdded; numChanged += numFieldsChanged; h_.writeText(""); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); h_.writeText(" "); int total = numRemoved + numAdded + numChanged; h_.writeText(" "); h_.writeText(""); h_.writeText("
TypeAdditionsChangesRemovalsTotal
Packages" + numPackagesAdded + "" + numPackagesChanged + "" + numPackagesRemoved + "" + numPackages + "
Classes and Interfaces" + numClassesAdded + "" + numClassesChanged + "" + numClassesRemoved + "" + numClasses + "
Constructors" + numCtorsAdded + "" + numCtorsChanged + "" + numCtorsRemoved + "" + numCtors + "
Methods" + numMethodsAdded + "" + numMethodsChanged + "" + numMethodsRemoved + "" + numMethods + "
Fields" + numFieldsAdded + "" + numFieldsChanged + "" + numFieldsRemoved + "" + numFields + "
Total" + numAdded + "" + numChanged + "" + numRemoved + "" + total + "
"); } }