1package jdiff;
2
3import java.util.*;
4import java.io.*;
5import 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
15public class HTMLStatistics {
16
17    /** Constructor. */
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
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     */
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     */
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     */
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