1 package jdiff; 2 3 import java.util.*; 4 import java.io.*; 5 import java.text.*; 6 7 /** 8 * Emit an HTML file containing statistics about the differences. 9 * Statistical information only appears if the -stats argument is used. 10 * 11 * See the file LICENSE.txt for copyright details. 12 * @author Matthew Doar, mdoar@pobox.com 13 */ 14 15 public class HTMLStatistics { 16 17 /** Constructor. */ HTMLStatistics(HTMLReportGenerator h)18 public HTMLStatistics(HTMLReportGenerator h) { 19 h_ = h; 20 } 21 22 /** The HTMLReportGenerator instance used to write HTML. */ 23 private HTMLReportGenerator h_ = null; 24 25 /** 26 * Emit the statistics HTML file. 27 */ 28 emitStatistics(String filename, APIDiff apiDiff)29 public void emitStatistics(String filename, APIDiff apiDiff) { 30 try { 31 FileOutputStream fos = new FileOutputStream(filename); 32 h_.reportFile = new PrintWriter(fos); 33 // Write out the HTML header 34 h_.writeStartHTMLHeader(); 35 String oldAPIName = "Old API"; 36 if (apiDiff.oldAPIName_ != null) 37 oldAPIName = apiDiff.oldAPIName_; 38 String newAPIName = "New API"; 39 if (apiDiff.newAPIName_ != null) 40 newAPIName = apiDiff.newAPIName_; 41 // Write out the title 42 h_.writeHTMLTitle("API Change Statistics"); 43 h_.writeStyleSheetRef(); 44 h_.writeText("</HEAD>"); 45 h_.writeText("<body class=\"gc-documentation\">"); 46 47 // writeText("<div class=\"g-section g-tpl-180\">"); 48 // Add the nav bar for the summary page 49 50 51 // Write a customized navigation bar for the statistics page 52 h_.writeText("<!-- Start of nav bar -->"); 53 54 SimpleDateFormat formatter 55 = new SimpleDateFormat ("yyyy.MM.dd HH:mm"); 56 Date day = new Date(); 57 58 h_.writeText("<a name=\"top\"></a>"); 59 h_.writeText("<div id=\"header\" style=\"margin-bottom:0;xborder-bottom:none;\">"); 60 h_.writeText("<div id=\"headerLeft\">"); 61 h_.writeText("<a href=\"../../../../index.html\" tabindex=\"-1\" target=\"_top\"><img src=\"../../../../assets/images/bg_logo.png\" alt=\"Android Developers\" /></a>"); 62 h_.writeText("</div>"); 63 h_.writeText(" <div id=\"headerRight\">"); 64 h_.writeText(" <div id=\"headerLinks\">"); 65 h_.writeText("<!-- <img src=\"/assets/images/icon_world.jpg\" alt=\"\" /> -->"); 66 h_.writeText("<span class=\"text\">"); 67 h_.writeText("<!-- <a href=\"#\">English</a> | -->"); 68 h_.writeText("<nobr><a href=\"https://developer.android.com\" target=\"_top\">Android Developers</a> | <a href=\"https://www.android.com\" target=\"_top\">Android.com</a></nobr>"); 69 h_.writeText("</span>"); 70 h_.writeText("</div>"); 71 h_.writeText(" <div class=\"and-diff-id\" style=\"margin-top:6px;margin-right:8px;\">"); 72 h_.writeText(" <table class=\"diffspectable\">"); 73 h_.writeText(" <tr>"); 74 h_.writeText(" <td colspan=\"2\" class=\"diffspechead\">API Diff Specification</td>"); 75 h_.writeText(" </tr>"); 76 h_.writeText(" <tr>"); 77 h_.writeText(" <td class=\"diffspec\" style=\"padding-top:.25em\">To Level:</td>"); 78 h_.writeText(" <td class=\"diffvaluenew\" style=\"padding-top:.25em\">" + newAPIName + "</td>"); 79 h_.writeText(" </tr>"); 80 h_.writeText(" <tr>"); 81 h_.writeText(" <td class=\"diffspec\">From Level:</td>"); 82 h_.writeText(" <td class=\"diffvalueold\">" + oldAPIName + "</td>"); 83 h_.writeText(" </tr>"); 84 h_.writeText(" <tr>"); 85 h_.writeText(" <td class=\"diffspec\">Generated</td>"); 86 h_.writeText(" <td class=\"diffvalue\">" + formatter.format( day ) + "</td>"); 87 h_.writeText(" </tr>"); 88 h_.writeText(" </table>"); 89 h_.writeText(" </div><!-- End and-diff-id -->"); 90 91 h_.writeText(" <div class=\"and-diff-id\" style=\"margin-right:8px;\">"); 92 h_.writeText(" <table class=\"diffspectable\">"); 93 h_.writeText(" <tr>"); 94 h_.writeText(" <td class=\"diffspec\" colspan=\"2\"><a href=\"../changes.html\" target=\"_top\">Top of Report</a>"); 95 h_.writeText(" </tr>"); 96 h_.writeText(" </table>"); 97 h_.writeText(" </div> <!-- End and-diff-id -->"); 98 99 h_.writeText(" </div> <!-- End headerRight -->"); 100 h_.writeText(" </div> <!-- End header -->"); 101 h_.writeText("<div id=\"body-content\">"); 102 h_.writeText("<div id=\"doc-content\" style=\"position:relative;\">"); 103 h_.writeText("<div id=\"mainBodyFluid\">"); 104 105 h_.writeText("<h1>API Change Statistics</h1>"); 106 107 108 DecimalFormat df2 = new DecimalFormat( "#,###,###,##0.00" ); 109 double dd = apiDiff.pdiff; 110 double dd2dec = new Double(df2.format(dd)).doubleValue(); 111 112 h_.writeText("<p>The overall difference between API Levels " + oldAPIName + " and " + newAPIName + " is approximately <span style=\"color:222;font-weight:bold;\">" + dd2dec + "%</span>."); 113 h_.writeText("</p>"); 114 115 h_.writeText("<br>"); 116 h_.writeText("<a name=\"numbers\"></a>"); 117 h_.writeText("<h2>Total of Differences, by Number and Type</h2>"); 118 h_.writeText("<p>"); 119 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."); 120 h_.writeText("</p>"); 121 122 emitNumbersByElement(apiDiff); 123 124 h_.writeText("<br>"); 125 h_.writeText("<a name=\"packages\"></a>"); 126 h_.writeText("<h2>Changed Packages, Sorted by Percentage Difference</h2>"); 127 emitPackagesByDiff(apiDiff); 128 h_.writeText("<p style=\"font-size:10px\">* See <a href=\"#calculation\">Calculation of Change Percentages</a>, below.</p>"); 129 130 h_.writeText("<br>"); 131 h_.writeText("<a name=\"classes\"></a>"); 132 h_.writeText("<h2>Changed Classes and <i>Interfaces</i>, Sorted by Percentage Difference</h2>"); 133 emitClassesByDiff(apiDiff); 134 h_.writeText("<p style=\"font-size:10px\">* See <a href=\"#calculation\">Calculation of Change Percentages</a>, below.</p>"); 135 136 h_.writeText("<br>"); 137 h_.writeText("<h2 id=\"calculation\">Calculation of Change Percentages</h2>"); 138 h_.writeText("<p>"); 139 h_.writeText("The percent change statistic reported for all elements in the "to" API Level specification is defined recursively as follows:</p>"); 140 h_.writeText("<pre>"); 141 h_.writeText("Percentage difference = 100 * (added + removed + 2*changed)"); 142 h_.writeText(" -----------------------------------"); 143 h_.writeText(" sum of public elements in BOTH APIs"); 144 h_.writeText("</pre>"); 145 h_.writeText("<p>where <code>added</code> is the number of packages added, <code>removed</code> is the number of packages removed, and <code>changed</code> is the number of packages changed."); 146 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."); 147 h_.writeText("The definition ensures that if all packages are removed and all new packages are"); 148 h_.writeText("added, the change will be 100%.</p>"); 149 150 h_.writeText(" </div> "); 151 h_.writeText(" <div id=\"footer\">"); 152 h_.writeText(" <div id=\"copyright\">"); 153 h_.writeText(" Except as noted, this content is licensed under "); 154 h_.writeText(" <a href=\"https://creativecommons.org/licenses/by/2.5/\"> Creative Commons Attribution 2.5</a>."); 155 h_.writeText(" For details and restrictions, see the <a href=\"https://developer.android.com/license.html\">Content License</a>."); 156 h_.writeText(" </div>"); 157 h_.writeText(" <div id=\"footerlinks\">"); 158 h_.writeText(" <p>"); 159 h_.writeText(" <a href=\"https://www.android.com/terms.html\">Site Terms of Service</a> -"); 160 h_.writeText(" <a href=\"https://www.android.com/privacy.html\">Privacy Policy</a> -"); 161 h_.writeText(" <a href=\"https://www.android.com/branding.html\">Brand Guidelines</a>"); 162 h_.writeText(" </p>"); 163 h_.writeText(" </div>"); 164 h_.writeText(" </div> <!-- end footer -->"); 165 h_.writeText(" </div><!-- end doc-content -->"); 166 h_.writeText(" </div> <!-- end body-content --> "); 167 168 h_.writeText("<script src=\"https://www.google-analytics.com/ga.js\" type=\"text/javascript\">"); 169 h_.writeText("</script>"); 170 h_.writeText("<script type=\"text/javascript\">"); 171 h_.writeText(" try {"); 172 h_.writeText(" var pageTracker = _gat._getTracker(\"UA-5831155-1\");"); 173 h_.writeText(" pageTracker._setAllowAnchor(true);"); 174 h_.writeText(" pageTracker._initData();"); 175 h_.writeText(" pageTracker._trackPageview();"); 176 h_.writeText(" } catch(e) {}"); 177 h_.writeText("</script>"); 178 179 h_.writeText("</BODY></HTML>"); 180 h_.reportFile.close(); 181 } catch(IOException e) { 182 System.out.println("IO Error while attempting to create " + filename); 183 System.out.println("Error: " + e.getMessage()); 184 System.exit(1); 185 } 186 } 187 188 /** 189 * Emit all packages sorted by percentage difference, and a histogram 190 * of the values. 191 */ emitPackagesByDiff(APIDiff apiDiff)192 public void emitPackagesByDiff(APIDiff apiDiff) { 193 194 Collections.sort(apiDiff.packagesChanged, new ComparePkgPdiffs()); 195 196 // Write out the table start 197 h_.writeText("<TABLE summary=\"Packages sorted by percentage difference\" WIDTH=\"100%\">"); 198 h_.writeText("<TR>"); 199 h_.writeText(" <TH WIDTH=\"10%\">Percentage Difference*</TH>"); 200 h_.writeText(" <TH>Package</TH>"); 201 h_.writeText("</TR>"); 202 203 int[] hist = new int[101]; 204 for (int i = 0; i < 101; i++) { 205 hist[i] = 0; 206 } 207 208 Iterator iter = apiDiff.packagesChanged.iterator(); 209 while (iter.hasNext()) { 210 PackageDiff pkg = (PackageDiff)(iter.next()); 211 int bucket = (int)(pkg.pdiff); 212 hist[bucket]++; 213 h_.writeText("<TR>"); 214 if (bucket != 0) 215 h_.writeText(" <TD ALIGN=\"center\">" + bucket + "</TD>"); 216 else 217 h_.writeText(" <TD ALIGN=\"center\"><1</TD>"); 218 h_.writeText(" <TD><A HREF=\"pkg_" + pkg.name_ + h_.reportFileExt + "\">" + pkg.name_ + "</A></TD>"); 219 h_.writeText("</TR>"); 220 } 221 222 h_.writeText("</TABLE>"); 223 224 /* Emit the histogram of the results 225 h_.writeText("<hr>"); 226 h_.writeText("<p><a name=\"packages_hist\"></a>"); 227 h_.writeText("<TABLE summary=\"Histogram of the package percentage differences\" BORDER=\"1\" cellspacing=\"0\" cellpadding=\"0\">"); 228 h_.writeText("<TR>"); 229 h_.writeText(" <TD ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Percentage<br>Difference</b></FONT></TD>"); 230 h_.writeText(" <TD ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Frequency</b></FONT></TD>"); 231 h_.writeText(" <TD width=\"300\" ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Percentage Frequency</b></FONT></TD>"); 232 h_.writeText("</TR>"); 233 234 double total = 0; 235 for (int i = 0; i < 101; i++) { 236 total += hist[i]; 237 } 238 for (int i = 0; i < 101; i++) { 239 if (hist[i] != 0) { 240 h_.writeText("<TR>"); 241 h_.writeText(" <TD ALIGN=\"center\">" + i + "</TD>"); 242 h_.writeText(" <TD>" + (hist[i]/total) + "</TD>"); 243 h_.writeText(" <TD><img alt=\"|\" src=\"../black.gif\" height=20 width=" + (hist[i]*300/total) + "></TD>"); 244 h_.writeText("</TR>"); 245 } 246 } 247 // Repeat the data in a format which is easier for spreadsheets 248 h_.writeText("<!-- START_PACKAGE_HISTOGRAM"); 249 for (int i = 0; i < 101; i++) { 250 if (hist[i] != 0) { 251 h_.writeText(i + "," + (hist[i]/total)); 252 } 253 } 254 h_.writeText("END_PACKAGE_HISTOGRAM -->"); 255 256 h_.writeText("</TABLE>"); 257 */ 258 } 259 260 /** 261 * Emit all classes sorted by percentage difference, and a histogram 262 * of the values.. 263 */ emitClassesByDiff(APIDiff apiDiff)264 public void emitClassesByDiff(APIDiff apiDiff) { 265 // Add all the changed classes to a list 266 List allChangedClasses = new ArrayList(); 267 Iterator iter = apiDiff.packagesChanged.iterator(); 268 while (iter.hasNext()) { 269 PackageDiff pkg = (PackageDiff)(iter.next()); 270 if (pkg.classesChanged != null) { 271 // Add the package name to the class name 272 List cc = new ArrayList(pkg.classesChanged); 273 Iterator iter2 = cc.iterator(); 274 while (iter2.hasNext()) { 275 ClassDiff classDiff = (ClassDiff)(iter2.next()); 276 classDiff.name_ = pkg.name_ + "." + classDiff.name_; 277 } 278 allChangedClasses.addAll(cc); 279 } 280 } 281 Collections.sort(allChangedClasses, new CompareClassPdiffs()); 282 283 // Write out the table start 284 h_.writeText("<TABLE summary=\"Classes sorted by percentage difference\" WIDTH=\"100%\">"); 285 h_.writeText("<TR WIDTH=\"20%\">"); 286 h_.writeText(" <TH WIDTH=\"10%\">Percentage<br>Difference*</TH>"); 287 h_.writeText(" <TH><b>Class or <i>Interface</i></b></TH>"); 288 h_.writeText("</TR>"); 289 290 int[] hist = new int[101]; 291 for (int i = 0; i < 101; i++) { 292 hist[i] = 0; 293 } 294 295 iter = allChangedClasses.iterator(); 296 while (iter.hasNext()) { 297 ClassDiff classDiff = (ClassDiff)(iter.next()); 298 int bucket = (int)(classDiff.pdiff); 299 hist[bucket]++; 300 h_.writeText("<TR>"); 301 if (bucket != 0) 302 h_.writeText(" <TD ALIGN=\"center\">" + bucket + "</TD>"); 303 else 304 h_.writeText(" <TD ALIGN=\"center\"><1</TD>"); 305 h_.writeText(" <TD><A HREF=\"" + classDiff.name_ + h_.reportFileExt + "\">"); 306 if (classDiff.isInterface_) 307 h_.writeText("<i>" + classDiff.name_ + "</i></A></TD>"); 308 else 309 h_.writeText(classDiff.name_ + "</A></TD>"); 310 h_.writeText("</TR>"); 311 } 312 313 h_.writeText("</TABLE>"); 314 315 /* Emit the histogram of the results 316 h_.writeText("<hr>"); 317 h_.writeText("<p><a name=\"classes_hist\"></a>"); 318 h_.writeText("<TABLE summary=\"Histogram of the class percentage differences\" BORDER=\"1\" cellspacing=\"0\" cellpadding=\"0\">"); 319 h_.writeText("<TR>"); 320 h_.writeText(" <TD ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Percentage<br>Difference</b></FONT></TD>"); 321 h_.writeText(" <TD ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Frequency</b></FONT></TD>"); 322 h_.writeText(" <TD width=\"300\" ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Percentage Frequency</b></FONT></TD>"); 323 h_.writeText("</TR>"); 324 325 double total = 0; 326 for (int i = 0; i < 101; i++) { 327 total += hist[i]; 328 } 329 for (int i = 0; i < 101; i++) { 330 if (hist[i] != 0) { 331 h_.writeText("<TR>"); 332 h_.writeText(" <TD ALIGN=\"center\">" + i + "</TD>"); 333 h_.writeText(" <TD>" + (hist[i]/total) + "</TD>"); 334 h_.writeText(" <TD><img alt=\"|\" src=\"../black.gif\" height=20 width=" + (hist[i]*300/total) + "></TD>"); 335 h_.writeText("</TR>"); 336 } 337 } 338 // Repeat the data in a format which is easier for spreadsheets 339 h_.writeText("<!-- START_CLASS_HISTOGRAM"); 340 for (int i = 0; i < 101; i++) { 341 if (hist[i] != 0) { 342 h_.writeText(i + "," + (hist[i]/total)); 343 } 344 } 345 h_.writeText("END_CLASS_HISTOGRAM -->"); 346 347 h_.writeText("</TABLE>"); 348 */ 349 } 350 351 /** 352 * Emit a table of numbers of removals, additions and changes by 353 * package, class, constructor, method and field. 354 */ emitNumbersByElement(APIDiff apiDiff)355 public void emitNumbersByElement(APIDiff apiDiff) { 356 357 // Local variables to hold the values 358 int numPackagesRemoved = apiDiff.packagesRemoved.size(); 359 int numPackagesAdded = apiDiff.packagesAdded.size(); 360 int numPackagesChanged = apiDiff.packagesChanged.size(); 361 362 int numClassesRemoved = 0; 363 int numClassesAdded = 0; 364 int numClassesChanged = 0; 365 366 int numCtorsRemoved = 0; 367 int numCtorsAdded = 0; 368 int numCtorsChanged = 0; 369 370 int numMethodsRemoved = 0; 371 int numMethodsAdded = 0; 372 int numMethodsChanged = 0; 373 374 int numFieldsRemoved = 0; 375 int numFieldsAdded = 0; 376 int numFieldsChanged = 0; 377 378 int numRemoved = 0; 379 int numAdded = 0; 380 int numChanged = 0; 381 382 // Calculate the values 383 Iterator iter = apiDiff.packagesChanged.iterator(); 384 while (iter.hasNext()) { 385 PackageDiff pkg = (PackageDiff)(iter.next()); 386 numClassesRemoved += pkg.classesRemoved.size(); 387 numClassesAdded += pkg.classesAdded.size(); 388 numClassesChanged += pkg.classesChanged.size(); 389 390 Iterator iter2 = pkg.classesChanged.iterator(); 391 while (iter2.hasNext()) { 392 ClassDiff classDiff = (ClassDiff)(iter2.next()); 393 numCtorsRemoved += classDiff.ctorsRemoved.size(); 394 numCtorsAdded += classDiff.ctorsAdded.size(); 395 numCtorsChanged += classDiff.ctorsChanged.size(); 396 397 numMethodsRemoved += classDiff.methodsRemoved.size(); 398 numMethodsAdded += classDiff.methodsAdded.size(); 399 numMethodsChanged += classDiff.methodsChanged.size(); 400 401 numFieldsRemoved += classDiff.fieldsRemoved.size(); 402 numFieldsAdded += classDiff.fieldsAdded.size(); 403 numFieldsChanged += classDiff.fieldsChanged.size(); 404 } 405 } 406 407 // Write out the table 408 h_.writeText("<TABLE summary=\"Number of differences\" WIDTH=\"100%\">"); 409 h_.writeText("<TR>"); 410 h_.writeText(" <th>Type</th>"); 411 h_.writeText(" <TH ALIGN=\"center\"><b>Additions</b></TH>"); 412 h_.writeText(" <TH ALIGN=\"center\"><b>Changes</b></TH>"); 413 h_.writeText(" <TH ALIGN=\"center\">Removals</TH>"); 414 h_.writeText(" <TH ALIGN=\"center\"><b>Total</b></TH>"); 415 h_.writeText("</TR>"); 416 417 h_.writeText("<TR>"); 418 h_.writeText(" <TD>Packages</TD>"); 419 420 h_.writeText(" <TD ALIGN=\"right\">" + numPackagesAdded + "</TD>"); 421 h_.writeText(" <TD ALIGN=\"right\">" + numPackagesChanged + "</TD>"); 422 h_.writeText(" <TD ALIGN=\"right\">" + numPackagesRemoved + "</TD>"); 423 int numPackages = numPackagesRemoved + numPackagesAdded + numPackagesChanged; 424 h_.writeText(" <TD ALIGN=\"right\">" + numPackages + "</TD>"); 425 h_.writeText("</TR>"); 426 427 numRemoved += numPackagesRemoved; 428 numAdded += numPackagesAdded; 429 numChanged += numPackagesChanged; 430 431 h_.writeText("<TR>"); 432 h_.writeText(" <TD>Classes and <i>Interfaces</i></TD>"); 433 434 h_.writeText(" <TD ALIGN=\"right\">" + numClassesAdded + "</TD>"); 435 h_.writeText(" <TD ALIGN=\"right\">" + numClassesChanged + "</TD>"); 436 h_.writeText(" <TD ALIGN=\"right\">" + numClassesRemoved + "</TD>"); 437 int numClasses = numClassesRemoved + numClassesAdded + numClassesChanged; 438 h_.writeText(" <TD ALIGN=\"right\">" + numClasses + "</TD>"); 439 h_.writeText("</TR>"); 440 441 numRemoved += numClassesRemoved; 442 numAdded += numClassesAdded; 443 numChanged += numClassesChanged; 444 445 h_.writeText("<TR>"); 446 h_.writeText(" <TD>Constructors</TD>"); 447 h_.writeText(" <TD ALIGN=\"right\">" + numCtorsAdded + "</TD>"); 448 h_.writeText(" <TD ALIGN=\"right\">" + numCtorsChanged + "</TD>"); 449 h_.writeText(" <TD ALIGN=\"right\">" + numCtorsRemoved + "</TD>"); 450 int numCtors = numCtorsRemoved + numCtorsAdded + numCtorsChanged; 451 h_.writeText(" <TD ALIGN=\"right\">" + numCtors + "</TD>"); 452 h_.writeText("</TR>"); 453 454 numRemoved += numCtorsRemoved; 455 numAdded += numCtorsAdded; 456 numChanged += numCtorsChanged; 457 458 h_.writeText("<TR>"); 459 h_.writeText(" <TD>Methods</TD>"); 460 h_.writeText(" <TD ALIGN=\"right\">" + numMethodsAdded + "</TD>"); 461 h_.writeText(" <TD ALIGN=\"right\">" + numMethodsChanged + "</TD>"); 462 h_.writeText(" <TD ALIGN=\"right\">" + numMethodsRemoved + "</TD>"); 463 int numMethods = numMethodsRemoved + numMethodsAdded + numMethodsChanged; 464 h_.writeText(" <TD ALIGN=\"right\">" + numMethods + "</TD>"); 465 h_.writeText("</TR>"); 466 467 numRemoved += numMethodsRemoved; 468 numAdded += numMethodsAdded; 469 numChanged += numMethodsChanged; 470 471 h_.writeText("<TR>"); 472 h_.writeText(" <TD>Fields</TD>"); 473 h_.writeText(" <TD ALIGN=\"right\">" + numFieldsAdded + "</TD>"); 474 h_.writeText(" <TD ALIGN=\"right\">" + numFieldsChanged + "</TD>"); 475 h_.writeText(" <TD ALIGN=\"right\">" + numFieldsRemoved + "</TD>"); 476 int numFields = numFieldsRemoved + numFieldsAdded + numFieldsChanged; 477 h_.writeText(" <TD ALIGN=\"right\">" + numFields + "</TD>"); 478 h_.writeText("</TR>"); 479 480 numRemoved += numFieldsRemoved; 481 numAdded += numFieldsAdded; 482 numChanged += numFieldsChanged; 483 484 h_.writeText("<TR>"); 485 h_.writeText(" <TD style=\"background-color:#FAFAFA\"><b>Total</b></TD>"); 486 487 h_.writeText(" <TD style=\"background-color:#FAFAFA\" ALIGN=\"right\"><strong>" + numAdded + "</strong></TD>"); 488 h_.writeText(" <TD style=\"background-color:#FAFAFA\" ALIGN=\"right\"><strong>" + numChanged + "</strong></TD>"); 489 h_.writeText(" <TD style=\"background-color:#FAFAFA\" ALIGN=\"right\"><strong>" + numRemoved + "</strong></TD>"); 490 int total = numRemoved + numAdded + numChanged; 491 h_.writeText(" <TD style=\"background-color:#FAFAFA\" ALIGN=\"right\"><strong>" + total + "</strong></TD>"); 492 h_.writeText("</TR>"); 493 494 h_.writeText("</TABLE>"); 495 } 496 497 } 498 499