• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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("<!-- &nbsp;<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&nbsp;Change&nbsp;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 &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.");
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 &quot;to&quot; 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\">&lt;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\">&lt;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