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("<div id=\"gc-container\" style=\"padding-left:1em;padding-right:1em;\">"); 59 h_.writeText("<a name=\"top\"></a>"); 60 h_.writeText("<div id=\"gc-header\">"); 61 h_.writeText(" <div id=\"logo\" style=\"padding-left:1em;\">"); 62 h_.writeText(" <a href=\"../../../documentation.html\" target=\"_top\"><img style=\"border: 0;\" src=\"../../../assets-google/android-logo-sm.gif\" \"/></a>"); 63 h_.writeText(" </div> <!-- End logo -->"); 64 65 h_.writeText("<div class=\"and-diff-id\">"); 66 h_.writeText("<table class=\"diffspectable\">"); 67 h_.writeText("<tr>"); 68 h_.writeText(" <td colspan=\"2\" class=\"diffspechead\">API Diff Specification</td>"); 69 h_.writeText("</tr>"); 70 h_.writeText(" <tr>"); 71 h_.writeText(" <td class=\"diffspec\">To Version:</td>"); 72 h_.writeText(" <td class=\"diffvaluenew\">" + newAPIName + "</td>"); 73 h_.writeText(" </tr>"); 74 h_.writeText(" <tr>"); 75 h_.writeText(" <td class=\"diffspec\">From Version:</td>"); 76 h_.writeText(" <td class=\"diffvalueold\">" + oldAPIName + "</td>"); 77 h_.writeText(" </tr>"); 78 h_.writeText("<tr>"); 79 h_.writeText(" <td class=\"diffspec\">Generated</td>"); 80 h_.writeText(" <td class=\"diffvalue\">" + formatter.format( day ) + "</td>"); 81 h_.writeText("</tr>"); 82 h_.writeText("</table>"); 83 h_.writeText(" </div> <!-- End and-diff-id -->"); 84 85 h_.writeText(" <div class=\"and-diff-id\">"); 86 h_.writeText(" <table class=\"diffspectable\">"); 87 h_.writeText(" <tr>"); 88 h_.writeText(" <td class=\"diffspec\" colspan=\"2\"><a href=\"../changes.html\" target=\"_top\">Top of Report</a></div>"); 89 h_.writeText(" </tr>"); 90 h_.writeText(" </table>"); 91 h_.writeText(" </div> <!-- End and-diff-id -->"); 92 93 h_.writeText("</div> <!-- End gc-header -->"); 94 h_.writeText("<div id=\"codesiteContent\" style=\"margin-top: 70px;margin-bottom:80px;\">"); 95 96 // Write the title in the body with some formatting 97 h_.writeText("<div style=\"xborder:1px solid yellow;vertical-align:top;padding:1em;margin-left:0;text-align:left;\">"); 98 h_.writeText(" <H1 class=\"pagecontenth1\">API Change Statistics</H1>"); 99 h_.writeText("</div>"); 100 101 102 h_.writeText("<p>"); 103 h_.writeText("The percent change statistic reported for all elements in each API is defined recursively as follows:</p>"); 104 h_.writeText("<pre>"); 105 h_.writeText("Percentage difference = 100 * (added + removed + 2*changed)"); 106 h_.writeText(" -----------------------------------"); 107 h_.writeText(" sum of public elements in BOTH APIs"); 108 h_.writeText("</pre>"); 109 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."); 110 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."); 111 h_.writeText("The definition ensures that if all packages are removed and all new packages are"); 112 h_.writeText("added, the change will be 100%. Values are rounded here, so a value of 0% indicates a percentage difference of less than 0.5%.</p>"); 113 114 h_.writeText("<p>The overall difference between the two APIs is approximately <span style=\"color:222;font-weight:bold;\">" + (int)(apiDiff.pdiff) + "%</span>."); 115 h_.writeText("</p>"); 116 117 h_.writeText("<br><h2 class=\"pagecontenth2\">Contents</h2>"); 118 h_.writeText("<dl><dt><a href=\"#packages\">Changed Packages</a></dt> <dd>Sorted by percentage difference</dd>"); 119 h_.writeText("<dt><a href=\"#classes\">Changed Classes and <i>Interfaces</i></a></dt><dd>Sorted by percentage difference</dd>"); 120 h_.writeText("<dt><a href=\"#numbers\">Total of Differences</a></dt><dd>Listed by number and type</dd></dl>"); 121 122 h_.writeText("<br>"); 123 h_.writeText("<a name=\"packages\"></a>"); 124 h_.writeText("<h2 class=\"pagecontenth2\">Changed Packages, Sorted by Percentage Difference</h2>"); 125 emitPackagesByDiff(apiDiff); 126 127 h_.writeText("<br>"); 128 h_.writeText("<a name=\"classes\"></a>"); 129 h_.writeText("<h2 class=\"pagecontenth2\">Changed Classes and <i>Interfaces</i>, Sorted by Percentage Difference</h2>"); 130 emitClassesByDiff(apiDiff); 131 132 h_.writeText("<br>"); 133 h_.writeText("<a name=\"numbers\"></a>"); 134 h_.writeText("<h2 class=\"pagecontenth2\">Total of Differences, by Number and Type</h2>"); 135 h_.writeText("<p>"); 136 h_.writeText("The table below lists the numbers of program elements (packages, classes, constructors, methods, and fields) that were removed, added or changed. 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."); 137 h_.writeText("</p>"); 138 139 emitNumbersByElement(apiDiff); 140 141 h_.writeText("</div><!-- end codesitecontent -->"); 142 h_.writeText("<div style=\"padding-left: 10px; padding-right: 10px; margin-top: 0; padding-bottom: 15px;\">"); 143 h_.writeText(" <table style=\"width: 100%; border: none;\"><tr>"); 144 h_.writeText(" <td style=\"text-align:center;font-size: 10pt; border: none; color: ccc;\"> "); 145 h_.writeText(" <span>©2008 Google - "); 146 h_.writeText(" <a href=\"http://code.google.com\">Code Home</a> - "); 147 h_.writeText(" <a href=\"http://www.google.com/accounts/TOS\">Site Terms of Sservice</a> - "); 148 h_.writeText(" <a href=\"http://www.google.com/privacy.html\">Privacy Policy</a> "); 149 h_.writeText(" </span>"); 150 h_.writeText(" <div style=\"xborder 1px solid red;position:relative;margin-top:-2em;" ); 151 h_.writeText(" font-size:8pt;color:aaa;text-align:right;\">"); 152 h_.writeText(" <em>Generated by <a href=\"http://www.jdiff.org/\">JDiff</a></em><br><img "); 153 h_.writeText(" align=\"right\" src=\"../../../assets/jdiff_logo.gif\">"); 154 h_.writeText(" </span>"); 155 h_.writeText(" </td>"); 156 h_.writeText(" </tr></table>"); 157 h_.writeText("</div>"); 158 h_.writeText("</div><!-- end gc-containter -->"); 159 160 h_.writeText("<script src=\"http://www.google-analytics.com/ga.js\" type=\"text/javascript\">"); 161 h_.writeText("</script>"); 162 h_.writeText("<script type=\"text/javascript\">"); 163 h_.writeText(" try {"); 164 h_.writeText(" var pageTracker = _gat._getTracker(\"UA-18071-1\");"); 165 h_.writeText(" pageTracker._setAllowAnchor(true);"); 166 h_.writeText(" pageTracker._initData();"); 167 h_.writeText(" pageTracker._trackPageview();"); 168 h_.writeText(" } catch(e) {}"); 169 h_.writeText("</script>"); 170 171 h_.writeText("</BODY></HTML>"); 172 h_.reportFile.close(); 173 } catch(IOException e) { 174 System.out.println("IO Error while attempting to create " + filename); 175 System.out.println("Error: " + e.getMessage()); 176 System.exit(1); 177 } 178 } 179 180 /** 181 * Emit all packages sorted by percentage difference, and a histogram 182 * of the values. 183 */ emitPackagesByDiff(APIDiff apiDiff)184 public void emitPackagesByDiff(APIDiff apiDiff) { 185 186 Collections.sort(apiDiff.packagesChanged, new ComparePkgPdiffs()); 187 188 // Write out the table start 189 h_.writeText("<TABLE summary=\"Packages sorted by percentage difference\" BORDER=\"1\" WIDTH=\"100%\" cellspacing=\"0\" cellpadding=\"0\">"); 190 h_.writeText("<TR WIDTH=\"20%\">"); 191 h_.writeText(" <TH>Percentage<br>Difference</TH>"); 192 h_.writeText(" <TH>Package</TH>"); 193 h_.writeText("</TR>"); 194 195 int[] hist = new int[101]; 196 for (int i = 0; i < 101; i++) { 197 hist[i] = 0; 198 } 199 200 Iterator iter = apiDiff.packagesChanged.iterator(); 201 while (iter.hasNext()) { 202 PackageDiff pkg = (PackageDiff)(iter.next()); 203 int bucket = (int)(pkg.pdiff); 204 hist[bucket]++; 205 h_.writeText("<TR>"); 206 if (bucket != 0) 207 h_.writeText(" <TD ALIGN=\"center\">" + bucket + "</TD>"); 208 else 209 h_.writeText(" <TD ALIGN=\"center\"><1</TD>"); 210 h_.writeText(" <TD><A HREF=\"pkg_" + pkg.name_ + h_.reportFileExt + "\">" + pkg.name_ + "</A></TD>"); 211 h_.writeText("</TR>"); 212 } 213 214 h_.writeText("</TABLE>"); 215 216 /* Emit the histogram of the results 217 h_.writeText("<hr>"); 218 h_.writeText("<p><a name=\"packages_hist\"></a>"); 219 h_.writeText("<TABLE summary=\"Histogram of the package percentage differences\" BORDER=\"1\" cellspacing=\"0\" cellpadding=\"0\">"); 220 h_.writeText("<TR>"); 221 h_.writeText(" <TD ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Percentage<br>Difference</b></FONT></TD>"); 222 h_.writeText(" <TD ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Frequency</b></FONT></TD>"); 223 h_.writeText(" <TD width=\"300\" ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Percentage Frequency</b></FONT></TD>"); 224 h_.writeText("</TR>"); 225 226 double total = 0; 227 for (int i = 0; i < 101; i++) { 228 total += hist[i]; 229 } 230 for (int i = 0; i < 101; i++) { 231 if (hist[i] != 0) { 232 h_.writeText("<TR>"); 233 h_.writeText(" <TD ALIGN=\"center\">" + i + "</TD>"); 234 h_.writeText(" <TD>" + (hist[i]/total) + "</TD>"); 235 h_.writeText(" <TD><img alt=\"|\" src=\"../black.gif\" height=20 width=" + (hist[i]*300/total) + "></TD>"); 236 h_.writeText("</TR>"); 237 } 238 } 239 // Repeat the data in a format which is easier for spreadsheets 240 h_.writeText("<!-- START_PACKAGE_HISTOGRAM"); 241 for (int i = 0; i < 101; i++) { 242 if (hist[i] != 0) { 243 h_.writeText(i + "," + (hist[i]/total)); 244 } 245 } 246 h_.writeText("END_PACKAGE_HISTOGRAM -->"); 247 248 h_.writeText("</TABLE>"); 249 */ 250 } 251 252 /** 253 * Emit all classes sorted by percentage difference, and a histogram 254 * of the values.. 255 */ emitClassesByDiff(APIDiff apiDiff)256 public void emitClassesByDiff(APIDiff apiDiff) { 257 // Add all the changed classes to a list 258 List allChangedClasses = new ArrayList(); 259 Iterator iter = apiDiff.packagesChanged.iterator(); 260 while (iter.hasNext()) { 261 PackageDiff pkg = (PackageDiff)(iter.next()); 262 if (pkg.classesChanged != null) { 263 // Add the package name to the class name 264 List cc = new ArrayList(pkg.classesChanged); 265 Iterator iter2 = cc.iterator(); 266 while (iter2.hasNext()) { 267 ClassDiff classDiff = (ClassDiff)(iter2.next()); 268 classDiff.name_ = pkg.name_ + "." + classDiff.name_; 269 } 270 allChangedClasses.addAll(cc); 271 } 272 } 273 Collections.sort(allChangedClasses, new CompareClassPdiffs()); 274 275 // Write out the table start 276 h_.writeText("<TABLE summary=\"Classes sorted by percentage difference\" BORDER=\"1\" WIDTH=\"100%\" cellspacing=\"0\" cellpadding=\"0\">"); 277 h_.writeText("<TR WIDTH=\"20%\">"); 278 h_.writeText(" <TH><b>Percentage<br>Difference</b></TH>"); 279 h_.writeText(" <TH><b>Class or <i>Interface</i></b></TH>"); 280 h_.writeText("</TR>"); 281 282 int[] hist = new int[101]; 283 for (int i = 0; i < 101; i++) { 284 hist[i] = 0; 285 } 286 287 iter = allChangedClasses.iterator(); 288 while (iter.hasNext()) { 289 ClassDiff classDiff = (ClassDiff)(iter.next()); 290 int bucket = (int)(classDiff.pdiff); 291 hist[bucket]++; 292 h_.writeText("<TR>"); 293 if (bucket != 0) 294 h_.writeText(" <TD ALIGN=\"center\">" + bucket + "</TD>"); 295 else 296 h_.writeText(" <TD ALIGN=\"center\"><1</TD>"); 297 h_.writeText(" <TD><A HREF=\"" + classDiff.name_ + h_.reportFileExt + "\">"); 298 if (classDiff.isInterface_) 299 h_.writeText("<i>" + classDiff.name_ + "</i></A></TD>"); 300 else 301 h_.writeText(classDiff.name_ + "</A></TD>"); 302 h_.writeText("</TR>"); 303 } 304 305 h_.writeText("</TABLE>"); 306 307 /* Emit the histogram of the results 308 h_.writeText("<hr>"); 309 h_.writeText("<p><a name=\"classes_hist\"></a>"); 310 h_.writeText("<TABLE summary=\"Histogram of the class percentage differences\" BORDER=\"1\" cellspacing=\"0\" cellpadding=\"0\">"); 311 h_.writeText("<TR>"); 312 h_.writeText(" <TD ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Percentage<br>Difference</b></FONT></TD>"); 313 h_.writeText(" <TD ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Frequency</b></FONT></TD>"); 314 h_.writeText(" <TD width=\"300\" ALIGN=\"center\" bgcolor=\"#EEEEFF\"><FONT size=\"+1\"><b>Percentage Frequency</b></FONT></TD>"); 315 h_.writeText("</TR>"); 316 317 double total = 0; 318 for (int i = 0; i < 101; i++) { 319 total += hist[i]; 320 } 321 for (int i = 0; i < 101; i++) { 322 if (hist[i] != 0) { 323 h_.writeText("<TR>"); 324 h_.writeText(" <TD ALIGN=\"center\">" + i + "</TD>"); 325 h_.writeText(" <TD>" + (hist[i]/total) + "</TD>"); 326 h_.writeText(" <TD><img alt=\"|\" src=\"../black.gif\" height=20 width=" + (hist[i]*300/total) + "></TD>"); 327 h_.writeText("</TR>"); 328 } 329 } 330 // Repeat the data in a format which is easier for spreadsheets 331 h_.writeText("<!-- START_CLASS_HISTOGRAM"); 332 for (int i = 0; i < 101; i++) { 333 if (hist[i] != 0) { 334 h_.writeText(i + "," + (hist[i]/total)); 335 } 336 } 337 h_.writeText("END_CLASS_HISTOGRAM -->"); 338 339 h_.writeText("</TABLE>"); 340 */ 341 } 342 343 /** 344 * Emit a table of numbers of removals, additions and changes by 345 * package, class, constructor, method and field. 346 */ emitNumbersByElement(APIDiff apiDiff)347 public void emitNumbersByElement(APIDiff apiDiff) { 348 349 // Local variables to hold the values 350 int numPackagesRemoved = apiDiff.packagesRemoved.size(); 351 int numPackagesAdded = apiDiff.packagesAdded.size(); 352 int numPackagesChanged = apiDiff.packagesChanged.size(); 353 354 int numClassesRemoved = 0; 355 int numClassesAdded = 0; 356 int numClassesChanged = 0; 357 358 int numCtorsRemoved = 0; 359 int numCtorsAdded = 0; 360 int numCtorsChanged = 0; 361 362 int numMethodsRemoved = 0; 363 int numMethodsAdded = 0; 364 int numMethodsChanged = 0; 365 366 int numFieldsRemoved = 0; 367 int numFieldsAdded = 0; 368 int numFieldsChanged = 0; 369 370 int numRemoved = 0; 371 int numAdded = 0; 372 int numChanged = 0; 373 374 // Calculate the values 375 Iterator iter = apiDiff.packagesChanged.iterator(); 376 while (iter.hasNext()) { 377 PackageDiff pkg = (PackageDiff)(iter.next()); 378 numClassesRemoved += pkg.classesRemoved.size(); 379 numClassesAdded += pkg.classesAdded.size(); 380 numClassesChanged += pkg.classesChanged.size(); 381 382 Iterator iter2 = pkg.classesChanged.iterator(); 383 while (iter2.hasNext()) { 384 ClassDiff classDiff = (ClassDiff)(iter2.next()); 385 numCtorsRemoved += classDiff.ctorsRemoved.size(); 386 numCtorsAdded += classDiff.ctorsAdded.size(); 387 numCtorsChanged += classDiff.ctorsChanged.size(); 388 389 numMethodsRemoved += classDiff.methodsRemoved.size(); 390 numMethodsAdded += classDiff.methodsAdded.size(); 391 numMethodsChanged += classDiff.methodsChanged.size(); 392 393 numFieldsRemoved += classDiff.fieldsRemoved.size(); 394 numFieldsAdded += classDiff.fieldsAdded.size(); 395 numFieldsChanged += classDiff.fieldsChanged.size(); 396 } 397 } 398 399 // Write out the table 400 h_.writeText("<TABLE summary=\"Number of differences\" BORDER=\"1\" WIDTH=\"100%\" cellspacing=\"0\" cellpadding=\"0\">"); 401 h_.writeText("<TR>"); 402 h_.writeText(" <TH COLSPAN=5 NOWRAP>"); 403 h_.writeText(" Number of Differences</TH>"); 404 h_.writeText("</TR>"); 405 h_.writeText("<TR>"); 406 h_.writeText(" <TH> </TD>"); 407 h_.writeText(" <TH ALIGN=\"center\"><b>Removals</b></TH>"); 408 h_.writeText(" <TH ALIGN=\"center\"><b>Additions</b></TH>"); 409 h_.writeText(" <TH ALIGN=\"center\"><b>Changes</b></TH>"); 410 h_.writeText(" <TH ALIGN=\"center\"><b>Total</b></TH>"); 411 h_.writeText("</TR>"); 412 413 h_.writeText("<TR>"); 414 h_.writeText(" <TD>Packages</TD>"); 415 h_.writeText(" <TD ALIGN=\"right\">" + numPackagesRemoved + "</TD>"); 416 h_.writeText(" <TD ALIGN=\"right\">" + numPackagesAdded + "</TD>"); 417 h_.writeText(" <TD ALIGN=\"right\">" + numPackagesChanged + "</TD>"); 418 int numPackages = numPackagesRemoved + numPackagesAdded + numPackagesChanged; 419 h_.writeText(" <TD ALIGN=\"right\">" + numPackages + "</TD>"); 420 h_.writeText("</TR>"); 421 422 numRemoved += numPackagesRemoved; 423 numAdded += numPackagesAdded; 424 numChanged += numPackagesChanged; 425 426 h_.writeText("<TR>"); 427 h_.writeText(" <TD>Classes and <i>Interfaces</i></TD>"); 428 h_.writeText(" <TD ALIGN=\"right\">" + numClassesRemoved + "</TD>"); 429 h_.writeText(" <TD ALIGN=\"right\">" + numClassesAdded + "</TD>"); 430 h_.writeText(" <TD ALIGN=\"right\">" + numClassesChanged + "</TD>"); 431 int numClasses = numClassesRemoved + numClassesAdded + numClassesChanged; 432 h_.writeText(" <TD ALIGN=\"right\">" + numClasses + "</TD>"); 433 h_.writeText("</TR>"); 434 435 numRemoved += numClassesRemoved; 436 numAdded += numClassesAdded; 437 numChanged += numClassesChanged; 438 439 h_.writeText("<TR>"); 440 h_.writeText(" <TD>Constructors</TD>"); 441 h_.writeText(" <TD ALIGN=\"right\">" + numCtorsRemoved + "</TD>"); 442 h_.writeText(" <TD ALIGN=\"right\">" + numCtorsAdded + "</TD>"); 443 h_.writeText(" <TD ALIGN=\"right\">" + numCtorsChanged + "</TD>"); 444 int numCtors = numCtorsRemoved + numCtorsAdded + numCtorsChanged; 445 h_.writeText(" <TD ALIGN=\"right\">" + numCtors + "</TD>"); 446 h_.writeText("</TR>"); 447 448 numRemoved += numCtorsRemoved; 449 numAdded += numCtorsAdded; 450 numChanged += numCtorsChanged; 451 452 h_.writeText("<TR>"); 453 h_.writeText(" <TD>Methods</TD>"); 454 h_.writeText(" <TD ALIGN=\"right\">" + numMethodsRemoved + "</TD>"); 455 h_.writeText(" <TD ALIGN=\"right\">" + numMethodsAdded + "</TD>"); 456 h_.writeText(" <TD ALIGN=\"right\">" + numMethodsChanged + "</TD>"); 457 int numMethods = numMethodsRemoved + numMethodsAdded + numMethodsChanged; 458 h_.writeText(" <TD ALIGN=\"right\">" + numMethods + "</TD>"); 459 h_.writeText("</TR>"); 460 461 numRemoved += numMethodsRemoved; 462 numAdded += numMethodsAdded; 463 numChanged += numMethodsChanged; 464 465 h_.writeText("<TR>"); 466 h_.writeText(" <TD>Fields</TD>"); 467 h_.writeText(" <TD ALIGN=\"right\">" + numFieldsRemoved + "</TD>"); 468 h_.writeText(" <TD ALIGN=\"right\">" + numFieldsAdded + "</TD>"); 469 h_.writeText(" <TD ALIGN=\"right\">" + numFieldsChanged + "</TD>"); 470 int numFields = numFieldsRemoved + numFieldsAdded + numFieldsChanged; 471 h_.writeText(" <TD ALIGN=\"right\">" + numFields + "</TD>"); 472 h_.writeText("</TR>"); 473 474 numRemoved += numFieldsRemoved; 475 numAdded += numFieldsAdded; 476 numChanged += numFieldsChanged; 477 478 h_.writeText("<TR>"); 479 h_.writeText(" <TD><b>Total</b></TD>"); 480 h_.writeText(" <TD ALIGN=\"right\">" + numRemoved + "</TD>"); 481 h_.writeText(" <TD ALIGN=\"right\">" + numAdded + "</TD>"); 482 h_.writeText(" <TD ALIGN=\"right\">" + numChanged + "</TD>"); 483 int total = numRemoved + numAdded + numChanged; 484 h_.writeText(" <TD ALIGN=\"right\">" + total + "</TD>"); 485 h_.writeText("</TR>"); 486 487 h_.writeText("</TABLE>"); 488 } 489 490 } 491 492