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&nbsp;Change&nbsp;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 &mdash; 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>&copy;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\">&lt;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\">&lt;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>&nbsp;</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