• 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 HTML based on the changes between two sets of APIs.
9  *
10  * See the file LICENSE.txt for copyright details.
11  * @author Matthew Doar, mdoar@pobox.com
12  */
13 public class HTMLReportGenerator {
14 
15     /** Default constructor. */
HTMLReportGenerator()16     public HTMLReportGenerator() {
17     }
18 
19     /** The Comments object for existing comments. */
20     private Comments existingComments_ = null;
21 
22     /**
23      * The Comments object for freshly regenerated comments.
24      * This is populated during the generation of the report,
25      * and should be like existingComments_ but with unused comments
26      * marked as such, so that they can be commented out in XML when
27      * the new comments are written out to the comments file.
28      */
29     private Comments newComments_ = null;
30 
31     /**
32      * Accessor method for the freshly generated Comments object.
33      * The list of comments is sorted before the object is returned.
34      */
getNewComments()35     public Comments getNewComments() {
36         Collections.sort(newComments_.commentsList_);
37         return newComments_;
38     }
39 
40     /** Generate the report. */
generate(APIComparator comp, Comments existingComments)41     public void generate(APIComparator comp, Comments existingComments) {
42         String fullReportFileName = reportFileName;
43         if (outputDir != null)
44             fullReportFileName = outputDir + JDiff.DIR_SEP + reportFileName;
45         System.out.println("JDiff: generating HTML report into the file '" + fullReportFileName + reportFileExt + "' and the subdirectory '" + fullReportFileName + "'");
46         // May be null if no comments file exists yet
47         existingComments_ = existingComments;
48         // Where the new comments will be placed
49         newComments_ = new Comments();
50         // Writing to multiple files, so make sure the subdirectory exists
51         File opdir = new File(fullReportFileName);
52         if (!opdir.mkdir() && !opdir.exists()) {
53             System.out.println("Error: could not create the subdirectory '" + fullReportFileName + "'");
54             System.exit(3);
55         }
56 
57         // Emit the documentation difference files
58         if (!Diff.noDocDiffs) {
59             // Documentation differences, one file per package
60             Diff.emitDocDiffs(fullReportFileName);
61         }
62 
63         // This is the top-level summary file, first in the right hand frame
64         // or linked at the start to if no frames are used.
65         String changesSummaryName = fullReportFileName + JDiff.DIR_SEP +
66             reportFileName + "-summary" + reportFileExt;
67         apiDiff = comp.apiDiff;
68         try {
69             FileOutputStream fos = new FileOutputStream(changesSummaryName);
70             reportFile = new PrintWriter(fos);
71             writeStartHTMLHeader();
72             // Write out the title in he HTML header
73             String oldAPIName = "Old API";
74             if (apiDiff.oldAPIName_ != null)
75                 oldAPIName = apiDiff.oldAPIName_;
76             String newAPIName = "New API";
77             if (apiDiff.newAPIName_ != null)
78                 newAPIName = apiDiff.newAPIName_;
79             if (windowTitle == null)
80                 writeHTMLTitle("Android API Differences Report");
81             else
82                 writeHTMLTitle(windowTitle);
83             writeStyleSheetRef();
84             writeText("</HEAD>");
85 
86             writeText("<body class=\"gc-documentation\">");
87 
88            // writeText("<div class=\"g-section g-tpl-180\">");
89            // Add the nav bar for the summary page
90             writeNavigationBar(reportFileName + "-summary", null, null,
91                                null, 0, true,
92                                apiDiff.packagesRemoved.size() != 0,
93                                apiDiff.packagesAdded.size() != 0,
94                                apiDiff.packagesChanged.size() != 0);
95 
96             // Write the title in the body with some formatting
97             if (docTitle == null) {
98 	                //writeText("<center>");
99                 writeText("  <div id=\"titleAligner\" style=\"vertical-align:top;padding:1em;margin-left:0;text-align:left;\">");
100                 writeText("    <H1 class=\"pagecontenth1\">API&nbsp;Differences&nbsp;Report</H1>");
101                 writeText("  </div>");
102             } else {
103                 writeText("  <div id=\"titleAligner\" style=\"vertical-align:top;padding:1em;margin-left:0;text-align:left;\">");
104                 writeText("    <H1 class=\"pagecontenth1\">" + docTitle + "</H1>");
105                 writeText("  </div>");
106             }
107 
108             writeText("<p>This document details the changes in the Android framework API. It shows ");
109             writeText("additions, modifications, and removals for packages, classes, methods, and ");
110             writeText("fields. Each reference to an API change includes a brief description of the ");
111             writeText("API and an explanation of the change and suggested workaround, where available.</p>");
112 
113             writeText("<p>The differences described in this report are based a comparison of the APIs ");
114             writeText("whose versions are specified in the upper-right corner of this page. It compares a ");
115             writeText("newer \"to\" API to an older \"from\" version, noting any changes relative to the ");
116             writeText("older API. So, for example, indicated API removals are no longer present in the \"to\" ");
117             writeText("API.</p>");
118 
119             writeText("<p>To navigate the report, use the \"Select a Diffs Index\" and \"Filter the Index\" ");
120             writeText("controls on the left. The report uses text formatting to indicate <em>interface names</em>, ");
121             writeText("<a href= ><tt>links to reference documentation</tt></a>, and <a href= >links to change ");
122             writeText("description</a>. </p>");
123 
124             writeText("<p>For more information about the Android framework API and SDK, ");
125             writeText("see the <a href=\"http://code.google.com/android/index.html\" target=\"_top\">Android product site</a>.</p>");
126 
127             // Write the contents and the other files as well
128             writeReport(apiDiff);
129             writeHTMLFooter();
130             reportFile.close();
131         } catch(IOException e) {
132             System.out.println("IO Error while attempting to create " + changesSummaryName);
133             System.out.println("Error: " + e.getMessage());
134             System.exit(1);
135         }
136 
137         // Now generate all the other files for multiple frames.
138         //
139         // The top-level changes.html frames file where everything starts.
140         String tln = fullReportFileName + reportFileExt;
141         // The file for the top-left frame.
142         String tlf = fullReportFileName + JDiff.DIR_SEP +
143             "jdiff_topleftframe" + reportFileExt;
144         // The default file for the bottom-left frame is the one with the
145         // most information in it.
146         String allDiffsIndexName = fullReportFileName + JDiff.DIR_SEP +
147             "alldiffs_index";
148         // Other indexes for the bottom-left frame.
149         String packagesIndexName = fullReportFileName + JDiff.DIR_SEP +
150             "packages_index";
151         String classesIndexName = fullReportFileName + JDiff.DIR_SEP +
152             "classes_index";
153         String constructorsIndexName = fullReportFileName + JDiff.DIR_SEP +
154             "constructors_index";
155         String methodsIndexName = fullReportFileName + JDiff.DIR_SEP +
156             "methods_index";
157         String fieldsIndexName = fullReportFileName + JDiff.DIR_SEP +
158             "fields_index";
159 
160         HTMLFiles hf = new HTMLFiles(this);
161         hf.emitTopLevelFile(tln, apiDiff);
162         hf.emitTopLeftFile(tlf);
163         hf.emitHelp(fullReportFileName, apiDiff);
164         hf.emitStylesheet();
165 
166         HTMLIndexes h = new HTMLIndexes(this);
167         h.emitAllBottomLeftFiles(packagesIndexName, classesIndexName,
168                             constructorsIndexName, methodsIndexName,
169                             fieldsIndexName, allDiffsIndexName, apiDiff);
170 
171         if (doStats) {
172             // The file for the statistical report.
173             String sf = fullReportFileName + JDiff.DIR_SEP +
174                 "jdiff_statistics" + reportFileExt;
175             HTMLStatistics stats = new HTMLStatistics(this);
176             stats.emitStatistics(sf, apiDiff);
177         }
178     }
179 
180     /**
181      * Write the HTML report.
182      *
183      * The top section describes all the packages added (with links) and
184      * removed, and the changed packages section has links which takes you
185      * to a section for each package. This pattern continues for classes and
186      * constructors, methods and fields.
187      */
writeReport(APIDiff apiDiff)188     public void writeReport(APIDiff apiDiff) {
189 
190         // Report packages which were removed in the new API
191         if (apiDiff.packagesRemoved.size() != 0) {
192             writeTableStart("Removed Packages", 2);
193             Iterator iter = apiDiff.packagesRemoved.iterator();
194             while (iter.hasNext()) {
195                 PackageAPI pkgAPI = (PackageAPI)(iter.next());
196                 String pkgName = pkgAPI.name_;
197                 if (trace) System.out.println("Package " + pkgName + " was removed.");
198                 writePackageTableEntry(pkgName, 0, pkgAPI.doc_, false);
199             }
200             writeTableEnd();
201         }
202 
203         // Report packages which were added in the new API
204         if (apiDiff.packagesAdded.size() != 0) {
205             writeTableStart("Added Packages", 2);
206             Iterator iter = apiDiff.packagesAdded.iterator();
207             while (iter.hasNext()) {
208                 PackageAPI pkgAPI = (PackageAPI)(iter.next());
209                 String pkgName = pkgAPI.name_;
210                 if (trace) System.out.println("Package " + pkgName + " was added.");
211                 writePackageTableEntry(pkgName, 1, pkgAPI.doc_, false);
212             }
213             writeTableEnd();
214         }
215 
216         // Report packages which were changed in the new API
217         if (apiDiff.packagesChanged.size() != 0) {
218             // Emit a table of changed packages, with links to the file
219             // for each package.
220             writeTableStart("Changed Packages", 3);
221             Iterator iter = apiDiff.packagesChanged.iterator();
222             while (iter.hasNext()) {
223                 PackageDiff pkgDiff = (PackageDiff)(iter.next());
224                 String pkgName = pkgDiff.name_;
225                 if (trace) System.out.println("Package " + pkgName + " was changed.");
226                 writePackageTableEntry(pkgName, 2, null, false);
227             }
228             writeTableEnd();
229             writeText("<!-- End of API section -->");
230 
231             // Now emit a separate file for each changed package.
232             writeText("<!-- Start of packages section -->");
233             PackageDiff[] pkgDiffs = new PackageDiff[apiDiff.packagesChanged.size()];
234             pkgDiffs = (PackageDiff[])apiDiff.packagesChanged.toArray(pkgDiffs);
235             for (int i = 0; i < pkgDiffs.length; i++) {
236                 reportChangedPackage(pkgDiffs, i);
237             }
238         }
239             writeText("</div><!-- end pagecontent -->");
240             writeText("</div><!-- end codesitecontent -->");
241             writeText("<div style=\"padding-left: 10px; padding-right: 10px; margin-top: 0; padding-bottom: 15px;\">");
242             writeText("  <table style=\"width: 100%; border: none;\"><tr>");
243             writeText("    <td style=\"text-align:center;font-size: 10pt; border: none; color: ccc;\"> ");
244             writeText("      <span>&copy;2008 Google - ");
245             writeText("            <a href=\"http://code.google.com\">Code Home</a> - ");
246             writeText("            <a href=\"http://www.google.com/accounts/TOS\">Site Terms of Service</a> - ");
247             writeText("            <a href=\"http://www.google.com/privacy.html\">Privacy Policy</a> ");
248             writeText("      </span>");
249             writeText("      <div style=\"position:relative;margin-top:-2em;" );
250             writeText("        font-size:8pt;color:aaa;text-align:right;\">");
251             writeText("        <em>Generated by <a href=\"http://www.jdiff.org/\">JDiff</a></em><br><img ");
252             writeText("        align=\"right\" src=\"../../../assets/jdiff_logo.gif\">");
253             writeText("      </span>");
254             writeText("    </td>");
255             writeText(" </tr></table>");
256             writeText("</div>");
257             writeText("</div><!-- end gc-containter -->");
258 }
259 
260     /**
261      * Write out the details of a changed package in a separate file.
262      */
reportChangedPackage(PackageDiff[] pkgDiffs, int pkgIndex)263     public void reportChangedPackage(PackageDiff[] pkgDiffs, int pkgIndex) {
264         PackageDiff pkgDiff = pkgDiffs[pkgIndex];
265         String pkgName = pkgDiff.name_;
266 
267         PrintWriter oldReportFile = null;
268         oldReportFile = reportFile;
269         String localReportFileName = null;
270         try {
271             // Prefix package files with pkg_ because there may be a class
272             // with the same name.
273             localReportFileName = reportFileName + JDiff.DIR_SEP + "pkg_" + pkgName + reportFileExt;
274             if (outputDir != null)
275                 localReportFileName = outputDir + JDiff.DIR_SEP + localReportFileName;
276             FileOutputStream fos = new FileOutputStream(localReportFileName);
277             reportFile = new PrintWriter(fos);
278             writeStartHTMLHeader();
279             writeHTMLTitle(pkgName);
280             writeStyleSheetRef();
281             writeText("</HEAD>");
282             writeText("<BODY>");
283         } catch(IOException e) {
284             System.out.println("IO Error while attempting to create " + localReportFileName);
285             System.out.println("Error: "+ e.getMessage());
286             System.exit(1);
287         }
288 
289         String pkgRef = pkgName;
290         pkgRef = pkgRef.replace('.', '/');
291         pkgRef = newDocPrefix + pkgRef + "/package-summary";
292         // A link to the package in the new API
293         String linkedPkgName = "<A HREF=\"" + pkgRef + ".html\" target=\"_top\"><font size=\"+1\"><tt>" + pkgName + "</tt></font></A>";
294         String prevPkgRef = null;
295         if (pkgIndex != 0) {
296             prevPkgRef = "pkg_" + pkgDiffs[pkgIndex-1].name_ + reportFileExt;
297         }
298         // Create the HTML link to the next package
299         String nextPkgRef = null;
300         if (pkgIndex < pkgDiffs.length - 1) {
301             nextPkgRef = "pkg_" + pkgDiffs[pkgIndex+1].name_ + reportFileExt;
302         }
303 
304         writeSectionHeader("Package " + linkedPkgName, pkgName,
305                            prevPkgRef, nextPkgRef,
306                            null, 1,
307                            pkgDiff.classesRemoved.size() != 0,
308                            pkgDiff.classesAdded.size() != 0,
309                            pkgDiff.classesChanged.size() != 0);
310 
311         // Report changes in documentation
312         if (reportDocChanges && pkgDiff.documentationChange_ != null) {
313             String pkgDocRef = pkgName + "/package-summary";
314             pkgDocRef = pkgDocRef.replace('.', '/');
315             String oldPkgRef = pkgDocRef;
316             String newPkgRef = pkgDocRef;
317             if (oldDocPrefix != null)
318                 oldPkgRef = oldDocPrefix + oldPkgRef;
319             else
320                 oldPkgRef = null;
321             newPkgRef = newDocPrefix + newPkgRef;
322             if (oldPkgRef != null)
323                 pkgDiff.documentationChange_ += "<A HREF=\"" + oldPkgRef +
324                     ".html#package_description\" target=\"_self\"><font size=\"+1\"><tt>old</tt></font></A> to ";
325             else
326                 pkgDiff.documentationChange_ += "<font size=\"+1\"><tt>old</tt></font> to ";
327             pkgDiff.documentationChange_ += "<A HREF=\"" + newPkgRef +
328                 ".html#package_description\" target=\"_self\"><font size=\"+1\"><tt>new</tt></font></A>. ";
329             writeText(pkgDiff.documentationChange_);
330         }
331 
332         // Report classes which were removed in the new API
333         if (pkgDiff.classesRemoved.size() != 0) {
334             // Determine the title for this section
335             boolean hasClasses = false;
336             boolean hasInterfaces = false;
337             Iterator iter = pkgDiff.classesRemoved.iterator();
338             while (iter.hasNext()) {
339                 ClassAPI classAPI = (ClassAPI)(iter.next());
340                 if (classAPI.isInterface_)
341                     hasInterfaces = true;
342                 else
343                     hasClasses = true;
344             }
345             if (hasInterfaces && hasClasses)
346                 writeTableStart("Removed Classes and Interfaces", 2);
347             else if (!hasInterfaces && hasClasses)
348                      writeTableStart("Removed Classes", 2);
349             else if (hasInterfaces && !hasClasses)
350                      writeTableStart("Removed Interfaces", 2);
351             // Emit the table entries
352             iter = pkgDiff.classesRemoved.iterator();
353             while (iter.hasNext()) {
354                 ClassAPI classAPI = (ClassAPI)(iter.next());
355                 String className = classAPI.name_;
356                 if (trace) System.out.println("Class/Interface " + className + " was removed.");
357                 writeClassTableEntry(pkgName, className, 0, classAPI.isInterface_, classAPI.doc_, false);
358             }
359             writeTableEnd();
360         }
361 
362         // Report classes which were added in the new API
363         if (pkgDiff.classesAdded.size() != 0) {
364             // Determine the title for this section
365             boolean hasClasses = false;
366             boolean hasInterfaces = false;
367             Iterator iter = pkgDiff.classesAdded.iterator();
368             while (iter.hasNext()) {
369                 ClassAPI classAPI = (ClassAPI)(iter.next());
370                 if (classAPI.isInterface_)
371                     hasInterfaces = true;
372                 else
373                     hasClasses = true;
374             }
375             if (hasInterfaces && hasClasses)
376                 writeTableStart("Added Classes and Interfaces", 2);
377             else if (!hasInterfaces && hasClasses)
378                      writeTableStart("Added Classes", 2);
379             else if (hasInterfaces && !hasClasses)
380                      writeTableStart("Added Interfaces", 2);
381             // Emit the table entries
382             iter = pkgDiff.classesAdded.iterator();
383             while (iter.hasNext()) {
384                 ClassAPI classAPI = (ClassAPI)(iter.next());
385                 String className = classAPI.name_;
386                 if (trace) System.out.println("Class/Interface " + className + " was added.");
387                 writeClassTableEntry(pkgName, className, 1, classAPI.isInterface_, classAPI.doc_, false);
388             }
389             writeTableEnd();
390         }
391 
392         // Report classes which were changed in the new API
393         if (pkgDiff.classesChanged.size() != 0) {
394             // Determine the title for this section
395             boolean hasClasses = false;
396             boolean hasInterfaces = false;
397             Iterator iter = pkgDiff.classesChanged.iterator();
398             while (iter.hasNext()) {
399                 ClassDiff classDiff = (ClassDiff)(iter.next());
400                 if (classDiff.isInterface_)
401                     hasInterfaces = true;
402                 else
403                     hasClasses = true;
404             }
405             if (hasInterfaces && hasClasses)
406                 writeTableStart("Changed Classes and Interfaces", 2);
407             else if (!hasInterfaces && hasClasses)
408                      writeTableStart("Changed Classes", 2);
409             else if (hasInterfaces && !hasClasses)
410                      writeTableStart("Changed Interfaces", 2);
411             // Emit a table of changed classes, with links to the file
412             // for each class.
413             iter = pkgDiff.classesChanged.iterator();
414             while (iter.hasNext()) {
415                 ClassDiff classDiff = (ClassDiff)(iter.next());
416                 String className = classDiff.name_;
417                 if (trace) System.out.println("Package " + pkgDiff.name_ + ", class/Interface " + className + " was changed.");
418                 writeClassTableEntry(pkgName, className, 2, classDiff.isInterface_, null, false);
419             }
420             writeTableEnd();
421             // Now emit a separate file for each changed class and interface.
422             ClassDiff[] classDiffs = new ClassDiff[pkgDiff.classesChanged.size()];
423             classDiffs = (ClassDiff[])pkgDiff.classesChanged.toArray(classDiffs);
424             for (int k = 0; k < classDiffs.length; k++) {
425                 reportChangedClass(pkgName, classDiffs, k);
426             }
427         }
428 
429         writeSectionFooter(pkgName, prevPkgRef, nextPkgRef, null, 1);
430         writeHTMLFooter();
431         reportFile.close();
432         reportFile = oldReportFile;
433     }
434 
435     /**
436      * Write out the details of a changed class in a separate file.
437      */
reportChangedClass(String pkgName, ClassDiff[] classDiffs, int classIndex)438     public void reportChangedClass(String pkgName, ClassDiff[] classDiffs, int classIndex) {
439         ClassDiff classDiff = classDiffs[classIndex];
440         String className = classDiff.name_;
441 
442         PrintWriter oldReportFile = null;
443         oldReportFile = reportFile;
444         String localReportFileName = null;
445         try {
446             localReportFileName = reportFileName + JDiff.DIR_SEP + pkgName + "." + className + reportFileExt;
447             if (outputDir != null)
448                 localReportFileName = outputDir + JDiff.DIR_SEP + localReportFileName;
449             FileOutputStream fos = new FileOutputStream(localReportFileName);
450             reportFile = new PrintWriter(fos);
451             writeStartHTMLHeader();
452             writeHTMLTitle(pkgName + "." + className);
453             writeStyleSheetRef();
454             writeText("</HEAD>");
455             writeText("<BODY>");
456         } catch(IOException e) {
457             System.out.println("IO Error while attempting to create " + localReportFileName);
458             System.out.println("Error: "+ e.getMessage());
459             System.exit(1);
460         }
461 
462         String classRef = pkgName + "." + className;
463         classRef = classRef.replace('.', '/');
464         if (className.indexOf('.') != -1) {
465             classRef = pkgName + ".";
466             classRef = classRef.replace('.', '/');
467             classRef = newDocPrefix + classRef + className;
468         } else {
469             classRef = newDocPrefix + classRef;
470         }
471         // A link to the class in the new API
472         String linkedClassName = "<A HREF=\"" + classRef + ".html\" target=\"_top\"><font size=\"+1\"><tt>" + className + "</tt></font></A>";
473         String lcn = pkgName + "." + linkedClassName;
474         //Links to the previous and next classes
475         String prevClassRef = null;
476         if (classIndex != 0) {
477             prevClassRef = pkgName + "." + classDiffs[classIndex-1].name_ + reportFileExt;
478         }
479         // Create the HTML link to the next package
480         String nextClassRef = null;
481         if (classIndex < classDiffs.length - 1) {
482             nextClassRef = pkgName + "." + classDiffs[classIndex+1].name_ + reportFileExt;
483         }
484 
485         if (classDiff.isInterface_)
486             lcn = "Interface " + lcn;
487         else
488             lcn = "Class " + lcn;
489         boolean hasCtors = classDiff.ctorsRemoved.size() != 0 ||
490             classDiff.ctorsAdded.size() != 0 ||
491             classDiff.ctorsChanged.size() != 0;
492         boolean hasMethods = classDiff.methodsRemoved.size() != 0 ||
493             classDiff.methodsAdded.size() != 0 ||
494             classDiff.methodsChanged.size() != 0;
495         boolean hasFields = classDiff.fieldsRemoved.size() != 0 ||
496             classDiff.fieldsAdded.size() != 0 ||
497             classDiff.fieldsChanged.size() != 0;
498         writeSectionHeader(lcn, pkgName, prevClassRef, nextClassRef,
499                            className, 2,
500                            hasCtors, hasMethods, hasFields);
501 
502         if (classDiff.inheritanceChange_ != null)
503             writeText("<p><font xsize=\"+1\">" + classDiff.inheritanceChange_ + "</font>");
504 
505         // Report changes in documentation
506         if (reportDocChanges && classDiff.documentationChange_ != null) {
507             String oldClassRef = null;
508             if (oldDocPrefix != null) {
509                 oldClassRef = pkgName + "." + className;
510                 oldClassRef = oldClassRef.replace('.', '/');
511                 if (className.indexOf('.') != -1) {
512                     oldClassRef = pkgName + ".";
513                     oldClassRef = oldClassRef.replace('.', '/');
514                     oldClassRef = oldDocPrefix + oldClassRef + className;
515                 } else {
516                     oldClassRef = oldDocPrefix + oldClassRef;
517                 }
518             }
519             if (oldDocPrefix != null)
520                 classDiff.documentationChange_ += "<A HREF=\"" + oldClassRef +
521                     ".html\" target=\"_self\"><font size=\"+1\"><tt>old</tt></font></A> to ";
522             else
523                 classDiff.documentationChange_ += "<font size=\"+1\"><tt>old</tt></font> to ";
524             classDiff.documentationChange_ += "<A HREF=\"" + classRef +
525                 ".html\" target=\"_self\"><font size=\"+1\"><tt>new</tt></font></A>. ";
526             writeText(classDiff.documentationChange_);
527         }
528 
529         if (classDiff.modifiersChange_ != null)
530             writeText("<p>" + classDiff.modifiersChange_);
531 
532         reportAllCtors(pkgName, classDiff);
533         reportAllMethods(pkgName, classDiff);
534         reportAllFields(pkgName, classDiff);
535 
536         writeSectionFooter(pkgName, prevClassRef, nextClassRef, className, 2);
537         writeHTMLFooter();
538         reportFile.close();
539         reportFile = oldReportFile;
540     }
541 
542     /**
543      * Write out the details of constructors in a class.
544      */
reportAllCtors(String pkgName, ClassDiff classDiff)545     public void reportAllCtors(String pkgName, ClassDiff classDiff) {
546         String className = classDiff.name_;
547         writeText("<a NAME=\"constructors\"></a>"); // Named anchor
548         // Report ctors which were removed in the new API
549         if (classDiff.ctorsRemoved.size() != 0) {
550             writeTableStart("Removed Constructors", 2);
551             Iterator iter = classDiff.ctorsRemoved.iterator();
552             while (iter.hasNext()) {
553                 ConstructorAPI ctorAPI = (ConstructorAPI)(iter.next());
554                 String ctorType = ctorAPI.type_;
555                 if (ctorType.compareTo("void") == 0)
556                     ctorType = "";
557                 String id = className + "(" + ctorType + ")";
558                 if (trace) System.out.println("Constructor " + id + " was removed.");
559                 writeCtorTableEntry(pkgName, className, ctorType, 0, ctorAPI.doc_, false);
560             }
561             writeTableEnd();
562         }
563 
564         // Report ctors which were added in the new API
565         if (classDiff.ctorsAdded.size() != 0) {
566             writeTableStart("Added Constructors", 2);
567             Iterator iter = classDiff.ctorsAdded.iterator();
568             while (iter.hasNext()) {
569                 ConstructorAPI ctorAPI = (ConstructorAPI)(iter.next());
570                 String ctorType = ctorAPI.type_;
571                 if (ctorType.compareTo("void") == 0)
572                     ctorType = "";
573                 String id = className + "(" + ctorType + ")";
574                 if (trace) System.out.println("Constructor " + id + " was added.");
575                 writeCtorTableEntry(pkgName, className, ctorType, 1, ctorAPI.doc_, false);
576             }
577             writeTableEnd();
578         }
579 
580         // Report ctors which were changed in the new API
581         if (classDiff.ctorsChanged.size() != 0) {
582             // Emit a table of changed classes, with links to the section
583             // for each class.
584             writeTableStart("Changed Constructors", 3);
585             Iterator iter = classDiff.ctorsChanged.iterator();
586             while (iter.hasNext()) {
587                 MemberDiff memberDiff = (MemberDiff)(iter.next());
588                 if (trace) System.out.println("Constructor for " + className +
589                     " was changed from " + memberDiff.oldType_ + " to " +
590                     memberDiff.newType_);
591                 writeCtorChangedTableEntry(pkgName, className, memberDiff);
592             }
593             writeTableEnd();
594         }
595     }
596 
597     /**
598      * Write out the details of methods in a class.
599      */
reportAllMethods(String pkgName, ClassDiff classDiff)600     public void reportAllMethods(String pkgName, ClassDiff classDiff) {
601         writeText("<a NAME=\"methods\"></a>"); // Named anchor
602         String className = classDiff.name_;
603         // Report methods which were removed in the new API
604         if (classDiff.methodsRemoved.size() != 0) {
605             writeTableStart("Removed Methods", 2);
606             Iterator iter = classDiff.methodsRemoved.iterator();
607             while (iter.hasNext()) {
608                 MethodAPI methodAPI = (MethodAPI)(iter.next());
609                 String methodName = methodAPI.name_ + "(" + methodAPI.getSignature() + ")";
610                 if (trace) System.out.println("Method " + methodName + " was removed.");
611                 writeMethodTableEntry(pkgName, className, methodAPI, 0, methodAPI.doc_, false);
612             }
613             writeTableEnd();
614         }
615 
616         // Report methods which were added in the new API
617         if (classDiff.methodsAdded.size() != 0) {
618             writeTableStart("Added Methods", 2);
619             Iterator iter = classDiff.methodsAdded.iterator();
620             while (iter.hasNext()) {
621                 MethodAPI methodAPI = (MethodAPI)(iter.next());
622                 String methodName = methodAPI.name_ + "(" + methodAPI.getSignature() + ")";
623                 if (trace) System.out.println("Method " + methodName + " was added.");
624                 writeMethodTableEntry(pkgName, className, methodAPI, 1, methodAPI.doc_, false);
625             }
626             writeTableEnd();
627         }
628 
629         // Report methods which were changed in the new API
630         if (classDiff.methodsChanged.size() != 0) {
631             // Emit a table of changed methods.
632             writeTableStart("Changed Methods", 3);
633             Iterator iter = classDiff.methodsChanged.iterator();
634             while (iter.hasNext()) {
635                 MemberDiff memberDiff = (MemberDiff)(iter.next());
636                 if (trace) System.out.println("Method " + memberDiff.name_ +
637                       " was changed.");
638                 writeMethodChangedTableEntry(pkgName, className, memberDiff);
639             }
640             writeTableEnd();
641         }
642     }
643 
644     /**
645      * Write out the details of fields in a class.
646      */
reportAllFields(String pkgName, ClassDiff classDiff)647     public void reportAllFields(String pkgName, ClassDiff classDiff) {
648         writeText("<a NAME=\"fields\"></a>"); // Named anchor
649         String className = classDiff.name_;
650         // Report fields which were removed in the new API
651         if (classDiff.fieldsRemoved.size() != 0) {
652             writeTableStart("Removed Fields", 2);
653             Iterator iter = classDiff.fieldsRemoved.iterator();
654             while (iter.hasNext()) {
655                 FieldAPI fieldAPI = (FieldAPI)(iter.next());
656                 String fieldName = fieldAPI.name_;
657                 if (trace) System.out.println("Field " + fieldName + " was removed.");
658                 writeFieldTableEntry(pkgName, className, fieldAPI, 0, fieldAPI.doc_, false);
659             }
660             writeTableEnd();
661         }
662 
663         // Report fields which were added in the new API
664         if (classDiff.fieldsAdded.size() != 0) {
665             writeTableStart("Added Fields", 2);
666             Iterator iter = classDiff.fieldsAdded.iterator();
667             while (iter.hasNext()) {
668                 FieldAPI fieldAPI = (FieldAPI)(iter.next());
669                 String fieldName = fieldAPI.name_;
670                 if (trace) System.out.println("Field " + fieldName + " was added.");
671                 writeFieldTableEntry(pkgName, className, fieldAPI, 1, fieldAPI.doc_, false);
672             }
673             writeTableEnd();
674         }
675 
676         // Report fields which were changed in the new API
677         if (classDiff.fieldsChanged.size() != 0) {
678             // Emit a table of changed classes, with links to the section
679             // for each class.
680             writeTableStart("Changed Fields", 3);
681             Iterator iter = classDiff.fieldsChanged.iterator();
682             while (iter.hasNext()) {
683                 MemberDiff memberDiff = (MemberDiff)(iter.next());
684                 if (trace) System.out.println("Field " + pkgName + "." + className + "." + memberDiff.name_ + " was changed from " + memberDiff.oldType_ + " to " + memberDiff.newType_);
685                 writeFieldChangedTableEntry(pkgName, className, memberDiff);
686             }
687             writeTableEnd();
688         }
689 
690     }
691 
692     /**
693      * Write the start of the HTML header, together with the current
694      * date and time in an HTML comment.
695      */
writeStartHTMLHeaderWithDate()696     public void writeStartHTMLHeaderWithDate() {
697         writeStartHTMLHeader(true);
698     }
699 
700     /** Write the start of the HTML header. */
writeStartHTMLHeader()701     public void writeStartHTMLHeader() {
702         writeStartHTMLHeader(false);
703     }
704 
705     /** Write the start of the HTML header. */
writeStartHTMLHeader(boolean addDate)706     public void writeStartHTMLHeader(boolean addDate) {
707         writeText("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\"\"" + RootDocToXML.baseURI + "/TR/REC-html40/frameset.dtd\">");
708         writeText("<HTML>");
709         writeText("<HEAD>");
710         writeText("<meta name=\"generator\" content=\"JDiff v" + JDiff.version + "\">");
711         writeText("<!-- Generated by the JDiff Javadoc doclet -->");
712         writeText("<!-- (" + JDiff.jDiffLocation + ") -->");
713         if (addDate)
714             writeText("<!-- on " + new Date() + " -->");
715         writeText("<meta name=\"description\" content=\"" + JDiff.jDiffDescription + "\">");
716         writeText("<meta name=\"keywords\" content=\"" + JDiff.jDiffKeywords + "\">");
717     }
718 
719     /** Write the HTML title */
writeHTMLTitle(String title)720     public void writeHTMLTitle(String title) {
721         writeText("<TITLE>");
722         writeText(title);
723         writeText("</TITLE>");
724     }
725 
726     /**
727      * Write the HTML style sheet reference for files in the subdirectory.
728      */
writeStyleSheetRef()729     public void writeStyleSheetRef() {
730         writeStyleSheetRef(false);
731     }
732 
733     /**
734      * Write the HTML style sheet reference. If inSameDir is set, don't add
735      * "../" to the location.
736      */
737 
writeStyleSheetRef(boolean inSameDir)738     public void writeStyleSheetRef(boolean inSameDir) {
739         if (inSameDir) {
740             writeText("<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../assets/codesite/codesite.css\" />");
741             writeText("<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../assets/codesite/codesearch.css\" />");
742             writeText("<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../assets/codesite/semantic_headers.css\" />");
743             writeText("<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../assets/style.css\" />");
744             writeText("<LINK REL=\"stylesheet\" TYPE=\"text/css\" HREF=\"stylesheet-jdiff.css\" TITLE=\"Style\">");
745 	}
746         else {
747             writeText("<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../assets/codesite/codesite.css\" />");
748             writeText("<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../assets/codesite/codesearch.css\" />");
749             writeText("<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../assets/codesite/semantic_headers.css\" />");
750             writeText("<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../assets/style.css\" />");
751             writeText("<LINK REL=\"stylesheet\" TYPE=\"text/css\" HREF=\"../stylesheet-jdiff.css\" TITLE=\"Style\">");
752 	}
753 // This doesn't work in non-windows browsers, so have to change the stylesheet
754 //        writeText("<!-- Override the color choice for the navigation bar -->");
755 //        writeText("<STYLE>");
756 //        //writeText(".NavBarCell1     { background-color:#FFFF99;} /* palegoldenrod */");
757 //        writeText(".NavBarCell1     { background-color:#FFFFCC;} /*  */");
758 //        writeText("</STYLE>");
759     }
760 
761     /** Write the HTML footer. */
writeHTMLFooter()762     public void writeHTMLFooter() {
763     writeText("<script src=\"http://www.google-analytics.com/ga.js\" type=\"text/javascript\">");
764     writeText("</script>");
765     writeText("<script type=\"text/javascript\">");
766     writeText("  try {");
767     writeText("    var pageTracker = _gat._getTracker(\"UA-18071-1\");");
768     writeText("    pageTracker._setAllowAnchor(true);");
769     writeText("    pageTracker._initData();");
770     writeText("    pageTracker._trackPageview();");
771     writeText("  } catch(e) {}");
772     writeText("</script>");
773     writeText("</BODY>");
774     writeText("</HTML>");
775     }
776 
777     /**
778      * Write a section header, which includes a navigation bar.
779      *
780      * @param title Title of the header. Contains any links necessary.
781      * @param packageName The name of the current package, with no slashes or
782      *                    links in it. May be null
783      * @param prevElemLink An HTML link to the previous element (a package or
784      *                     class). May be null.
785      * @param nextElemLink An HTML link to the next element (a package or
786      *                     class). May be null.
787      * @param className The name of the current class, with no slashes or
788      *                  links in it. May be null.
789      * @param level 0 = overview, 1 = package, 2 = class/interface
790      */
writeSectionHeader(String title, String packageName, String prevElemLink, String nextElemLink, String className, int level, boolean hasRemovals, boolean hasAdditions, boolean hasChanges)791     public void writeSectionHeader(String title, String packageName,
792                                    String prevElemLink, String nextElemLink,
793                                    String className, int level,
794                                    boolean hasRemovals,
795                                    boolean hasAdditions,
796                                    boolean hasChanges) {
797         writeNavigationBar(packageName, prevElemLink, nextElemLink,
798                            className, level, true,
799                            hasRemovals, hasAdditions, hasChanges);
800         if (level != 0) {
801             reportFile.println("<H2>");
802             reportFile.println(title);
803             reportFile.println("</H2>");
804         }
805     }
806 
807     /**
808      * Write a section footer, which includes a navigation bar.
809      *
810      * @param packageName The name of the current package, with no slashes or
811      *                    links in it. may be null
812      * @param prevElemLink An HTML link to the previous element (a package or
813      *                     class). May be null.
814      * @param nextElemLink An HTML link to the next element (a package or
815      *                     class). May be null.
816      * @param className The name of the current class, with no slashes or
817      *                  links in it. May be null
818      * @param level 0 = overview, 1 = package, 2 = class/interface
819      */
writeSectionFooter(String packageName, String prevElemLink, String nextElemLink, String className, int level)820     public void writeSectionFooter(String packageName,
821                                    String prevElemLink, String nextElemLink,
822                                    String className, int level) {
823             writeText("</div><!-- end codesitecontent -->");
824             writeText("<div style=\"padding-left: 10px; padding-right: 10px; margin-top: 0; padding-bottom: 15px;\">");
825             writeText("  <table style=\"width: 100%; border: none;\"><tr>");
826             writeText("    <td style=\"text-align:center;font-size: 10pt; border: none; color: ccc;\"> ");
827             writeText("      <span>&copy;2008 Google - ");
828             writeText("            <a href=\"http://code.google.com\">Code Home</a> - ");
829             writeText("            <a href=\"http://www.google.com/accounts/TOS\">Site Terms of Service</a> - ");
830             writeText("            <a href=\"http://www.google.com/privacy.html\">Privacy Policy</a> ");
831             writeText("      </span>");
832             writeText("      <div style=\"xborder:1px solid red;position:relative;margin-top:-2em;" );
833             writeText("        font-size:8pt;color:aaa;text-align:right;\">");
834             writeText("        <em>Generated by <a href=\"http://www.jdiff.org/\">JDiff</a></em><br><img ");
835             writeText("        align=\"right\" src=\"../../../assets/jdiff_logo.gif\">");
836             writeText("      </span>");
837             writeText("    </td>");
838             writeText(" </tr></table>");
839             writeText("</div>");
840             writeText("</div><!-- end gc-containter -->");
841 /*
842         reportFile.println("<HR>");
843         writeNavigationBar(packageName, prevElemLink, nextElemLink,
844                            className, level, false,
845                            false, false, false);
846 */
847     }
848 
849     /**
850      * Write a navigation bar section header.
851      *
852      * @param pkgName The name of the current package, with no slashes or
853      *                links in it.
854      * @param prevElemLink An HTML link to the previous element (a package or
855      *                     class). May be null.
856      * @param nextElemLink An HTML link to the next element (a package or
857      *                     class). May be null.
858      * @param className The name of the current class, with no slashes or
859      *                links in it. May be null.
860      * @param level 0 = overview, 1 = package, 2 = class/interface
861      */
862 
writeNavigationBar(String pkgName, String prevElemLink, String nextElemLink, String className, int level, boolean upperNavigationBar, boolean hasRemovals, boolean hasAdditions, boolean hasChanges)863     public void writeNavigationBar(String pkgName,
864                                    String prevElemLink, String nextElemLink,
865                                    String className, int level,
866                                    boolean upperNavigationBar,
867                                    boolean hasRemovals, boolean hasAdditions,
868                                    boolean hasChanges) {
869 
870             String oldAPIName = "Old API";
871             if (apiDiff.oldAPIName_ != null)
872                 oldAPIName = apiDiff.oldAPIName_;
873             String newAPIName = "New API";
874             if (apiDiff.newAPIName_ != null)
875                 newAPIName = apiDiff.newAPIName_;
876 
877             SimpleDateFormat formatter
878               = new SimpleDateFormat ("yyyy.MM.dd HH:mm");
879             Date day = new Date();
880 
881 	    reportFile.println("<!-- Start of nav bar -->");
882 
883 	    reportFile.println("<div id=\"gc-container\" style=\"padding-left:1em;padding-right:1em;\" id=\"pagecontent\">");
884 	    reportFile.println("<a name=\"top\"></a>");
885 	    reportFile.println("<div id=\"gc-header\">");
886 	    reportFile.println("  <div id=\"logo\"  style=\"padding-left:1em;\">");
887 	    reportFile.println("    <a href=\"../../../documentation.html\" target=\"_top\"><img style=\"border: 0;\" src=\"../../../assets-google/android-logo-sm.gif\" \"/></a>");
888 	    reportFile.println("  </div> <!-- End logo -->");
889 	    reportFile.println("  <div class=\"and-diff-id\">");
890             reportFile.println("    <table class=\"diffspectable\">");
891 	    reportFile.println("      <tr>");
892 	    reportFile.println("        <td colspan=\"2\" class=\"diffspechead\">API Diff Specification</td>");
893 	    reportFile.println("      </tr>");
894 	    reportFile.println("      <tr>");
895 	    reportFile.println("        <td class=\"diffspec\" style=\"padding-top:.25em\">To Version:</td>");
896 	    reportFile.println("        <td class=\"diffvaluenew\" style=\"padding-top:.25em\">" + newAPIName + "</td>");
897 	    reportFile.println("      </tr>");
898 	    reportFile.println("      <tr>");
899 	    reportFile.println("        <td class=\"diffspec\">From Version:</td>");
900 	    reportFile.println("        <td class=\"diffvalueold\">" + oldAPIName + "</td>");
901 	    reportFile.println("      </tr>");
902 //	    reportFile.println("      <tr>");
903 //	    reportFile.println("        <td class=\"diffspec\">Product Type:</td>");
904 //	    reportFile.println("        <td class=\"diffvalue\">Generic</td>");
905 //	    reportFile.println("      </tr>");
906 	    reportFile.println("      <tr>");
907 	    reportFile.println("        <td class=\"diffspec\">Generated</td>");
908 	    reportFile.println("        <td class=\"diffvalue\">" + formatter.format( day ) + "</td>");
909 	    reportFile.println("      </tr>");
910  	    reportFile.println("    </table>");
911 	    reportFile.println("  </div> <!-- End and-diff-id -->");
912 
913             if (doStats) {
914 	    	reportFile.println("  <div class=\"and-diff-id\">");
915 	    	reportFile.println("    <table class=\"diffspectable\">");
916 	    	reportFile.println("      <tr>");
917 	    	reportFile.println("        <td class=\"diffspec\" colspan=\"2\"><a href=\"jdiff_statistics.html\">Statistics</a></div>");
918 	    	reportFile.println("      </tr>");
919  	    	reportFile.println("    </table>");
920 	    	reportFile.println("  </div> <!-- End and-diff-id -->");
921 	    }
922 
923 	    reportFile.println("</div> <!-- End gc-header -->");
924 	    reportFile.println("<div id=\"codesiteContent\" style=\"margin-top: 70px;margin-bottom:80px;\">");
925 
926 /*
927 	reportFile.println("<TABLE summary=\"Navigation bar\" BORDER=\"0\" WIDTH=\"100%\" CELLPADDING=\"1\" CELLSPACING=\"0\">");
928         reportFile.println("  <TR>");
929         reportFile.println("    <TD COLSPAN=2 BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\">");
930         reportFile.println("    <TABLE summary=\"Navigation bar\" BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"3\">");
931         reportFile.println("    <TR ALIGN=\"center\" VALIGN=\"top\">");
932         boolean atOverview = (level == 0);
933         boolean atPackage = (level == 1);
934         boolean atClass = (level == 2);
935 
936         // Always have a link to the Javadoc files
937         if (atOverview) {
938             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + newDocPrefix + "index.html\" target=\"_top\"><FONT CLASS=\"NavBarFont1\"><B><font size=\"+1\"><tt>" + apiDiff.newAPIName_ + "</tt></font></B></FONT></A>&nbsp;</TD>");
939         } else if (atPackage) {
940             String pkgRef = pkgName;
941             pkgRef = pkgRef.replace('.', '/');
942             pkgRef = newDocPrefix + pkgRef + "/package-summary";
943             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + pkgRef + ".html\" target=\"_top\"><FONT CLASS=\"NavBarFont1\"><B><font size=\"+1\"><tt>" + apiDiff.newAPIName_ + "</tt></font></B></FONT></A>&nbsp;</TD>");
944         } else if (atClass) {
945             String classRef = pkgName + "." + className;
946             classRef = classRef.replace('.', '/');
947             if (className.indexOf('.') != -1) {
948                 classRef = pkgName + ".";
949                 classRef = classRef.replace('.', '/');
950                 classRef = newDocPrefix + classRef + className;
951             } else {
952                 classRef = newDocPrefix + classRef;
953             }
954             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + classRef + ".html\" target=\"_top\"><FONT CLASS=\"NavBarFont1\"><B><font size=\"+1\"><tt>" + apiDiff.newAPIName_ + "</tt></font></B></FONT></A>&nbsp;</TD>");
955         }
956 
957         if (atOverview) {
958             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1Rev\"> &nbsp;<FONT CLASS=\"NavBarFont1Rev\"><B>Overview</B></FONT>&nbsp;</TD>");
959             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> &nbsp;<FONT CLASS=\"NavBarFont1\">Package</FONT>&nbsp;</TD>");
960             reportFile.println("      <TD BGCOLOR=\"#FFFFFF\" CLASS=\"NavBarCell1\"> &nbsp;<FONT CLASS=\"NavBarFont1\">Class</FONT>&nbsp;</TD>");
961         }
962 
963         String changesSummaryName = reportFileName + "-summary" + reportFileExt;
964         if (atPackage) {
965             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + changesSummaryName + "\"><FONT CLASS=\"NavBarFont1\"><B>Overview</B></FONT></A>&nbsp;</TD>");
966             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1Rev\"> &nbsp;<FONT CLASS=\"NavBarFont1Rev\"><B>Package</B></FONT>&nbsp;</TD>");
967             reportFile.println("      <TD BGCOLOR=\"#FFFFFF\" CLASS=\"NavBarCell1\"> &nbsp;<FONT CLASS=\"NavBarFont1\">Class</FONT>&nbsp;</TD>");
968         }
969         if (atClass) {
970             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + changesSummaryName + "\"><FONT CLASS=\"NavBarFont1\"><B>Overview</B></FONT></A>&nbsp;</TD>");
971             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"pkg_" + pkgName + reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Package</B></FONT></A>&nbsp;</TD>");
972             reportFile.println("      <TD BGCOLOR=\"#FFFFFF\" CLASS=\"NavBarCell1Rev\"> &nbsp;<FONT CLASS=\"NavBarFont1Rev\"><B>Class</B></FONT>&nbsp;</TD>");
973         }
974 
975         if (!Diff.noDocDiffs) {
976             if (atPackage) {
977                 String id = (String)Diff.firstDiffOutput.get(pkgName + "!package");
978                 if (id != null)
979                     reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + Diff.diffFileName + "index" + reportFileExt  + "#" + id + "\"><FONT CLASS=\"NavBarFont1\"><B>Text Changes</B></FONT></A>&nbsp;</TD>");
980                 else
981                     reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <FONT CLASS=\"NavBarFont1\">Text Changes</FONT>&nbsp;</TD>");
982             } else if (atClass) {
983                 String id = (String)Diff.firstDiffOutput.get(pkgName + "." + className + "!class");
984                 if (id != null)
985                     reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + Diff.diffFileName + "index" + reportFileExt  + "#" + id + "\"><FONT CLASS=\"NavBarFont1\"><B>Text Changes</B></FONT></A>&nbsp;</TD>");
986                 else
987                     reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <FONT CLASS=\"NavBarFont1\">Text Changes</FONT>&nbsp;</TD>");
988             } else {
989                 reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + Diff.diffFileName + "index" + reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Text Changes</B></FONT></A>&nbsp;</TD>");
990             }
991         }
992 
993         if (doStats) {
994             reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"jdiff_statistics" + reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Statistics</B></FONT></A>&nbsp;</TD>");
995         }
996 
997         // Always have a link to the JDiff help file
998         reportFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"jdiff_help" + reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Help</B></FONT></A>&nbsp;</TD>");
999         reportFile.println("    </TR>");
1000         reportFile.println("    </TABLE>");
1001         reportFile.println("  </TD>");
1002 
1003         // The right hand side title, only added at the top
1004         if (upperNavigationBar) {
1005             reportFile.println("  <TD ALIGN=\"right\" VALIGN=\"top\" ROWSPAN=3><EM><b>Generated by<br><a href=\"" + JDiff.jDiffLocation + "\" class=\"staysblack\" target=\"_top\">JDiff</a></b></EM></TD>");
1006         } else {
1007             reportFile.println("  <TD ALIGN=\"right\" VALIGN=\"top\" ROWSPAN=3></TD>");
1008         }
1009         reportFile.println("</TR>");
1010 
1011         // Links for frames and no frames
1012         reportFile.println("<TR>");
1013 
1014         // All of the previous and next links, and the frames and non-frames
1015         // links are in one table cell
1016         reportFile.println("  <TD BGCOLOR=\"" + bgcolor + "\" CLASS=\"NavBarCell2\"><FONT SIZE=\"-2\">");
1017         // Display links to the previous and next packages or classes
1018         if (atPackage || atClass) {
1019             String elemName = "CLASS";
1020             if (className == null) {
1021                 elemName = "PACKAGE";
1022             }
1023             if (prevElemLink == null) {
1024                 reportFile.println("&nbsp;<B>PREV " + elemName + "</B>&nbsp;");
1025             } else {
1026                 reportFile.println("&nbsp;<A HREF=\"" + prevElemLink + "\"><B>PREV " + elemName + "</B></A>");
1027             }
1028             if (nextElemLink == null) {
1029                 reportFile.println("&nbsp;<B>NEXT " + elemName + "</B>&nbsp;");
1030             } else {
1031                 reportFile.println("&nbsp;<A HREF=\"" + nextElemLink + "\"><B>NEXT " + elemName + "</B></A>");
1032             }
1033             reportFile.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
1034         } else {
1035             reportFile.println("  &nbsp;&nbsp;");
1036         }
1037         // Links for frames and non-frames.
1038         reportFile.println("  <A HREF=\"" + "../" + reportFileName + reportFileExt + "\" TARGET=\"_top\"><B>FRAMES</B></A>  &nbsp;");
1039         if (className == null) {
1040             if (level == 0) {
1041                 reportFile.println("  &nbsp;<A HREF=\"" + pkgName + reportFileExt + "\" TARGET=\"_top\"><B>NO FRAMES</B></A></FONT></TD>");
1042             } else {
1043                 reportFile.println("  &nbsp;<A HREF=\"pkg_" + pkgName + reportFileExt + "\" TARGET=\"_top\"><B>NO FRAMES</B></A></FONT></TD>");
1044             }
1045         } else {
1046             reportFile.println("  &nbsp;<A HREF=\"" + pkgName + "." + className + reportFileExt + "\" TARGET=\"_top\"><B>NO FRAMES</B></A></FONT></TD>");
1047         }
1048 
1049         // All of the details links are in one table cell
1050         if (atClass) {
1051             // Links to a class page's sections
1052             // The meaning of these three variable is overloaded
1053             boolean hasCtors = hasRemovals;
1054             boolean hasMethods = hasAdditions;
1055             boolean hasFields = hasChanges;
1056             if (hasCtors || hasMethods || hasFields) {
1057                 reportFile.println("  <TD BGCOLOR=\"" + bgcolor + "\" CLASS=\"NavBarCell3\"><FONT SIZE=\"-2\"> DETAIL: &nbsp;");
1058                 if (hasCtors) {
1059                     reportFile.println("<a href=\"#constructors\">CONSTRUCTORS</a>&nbsp;|&nbsp;");
1060                 } else {
1061                     reportFile.println("CONSTRUCTORS&nbsp;|&nbsp;");
1062                 }
1063                 if (hasMethods) {
1064                     reportFile.println("<a href=\"#methods\">METHODS</a>&nbsp;|&nbsp;");
1065                 } else {
1066                     reportFile.println("METHODS&nbsp;|&nbsp;");
1067                 }
1068                 if (hasFields) {
1069                     reportFile.println("<a href=\"#fields\">FIELDS</a>");
1070                 } else {
1071                     reportFile.println("FIELDS");
1072                 }
1073                 reportFile.println("  </FONT></TD>");
1074             } else {
1075                 // Make the end of the table line match the length of the top
1076                 reportFile.println("<TD BGCOLOR=\"0xFFFFFF\" CLASS=\"NavBarCell3\"></TD>");
1077             }
1078         } else {
1079             // Links to a package page's sections
1080             if (hasRemovals || hasAdditions || hasChanges) {
1081                 reportFile.println("  <TD BGCOLOR=\"" + bgcolor + "\" CLASS=\"NavBarCell3\"><FONT SIZE=\"-2\"> DETAIL: &nbsp;");
1082                 if (hasRemovals) {
1083                     reportFile.println("<a href=\"#Removed\">REMOVED</a>&nbsp;|&nbsp;");
1084                 } else {
1085                     reportFile.println("REMOVED&nbsp;|&nbsp;");
1086                 }
1087                 if (hasAdditions) {
1088                     reportFile.println("<a href=\"#Added\">ADDED</a>&nbsp;|&nbsp;");
1089                 } else {
1090                     reportFile.println("ADDED&nbsp;|&nbsp;");
1091                 }
1092                 if (hasChanges) {
1093                     reportFile.println("<a href=\"#Changed\">CHANGED</a>");
1094                 } else {
1095                     reportFile.println("CHANGED");
1096                 }
1097                 reportFile.println("  </FONT></TD>");
1098             } else {
1099                 // Make the end of the table line match the length of the top
1100                 reportFile.println("<TD BGCOLOR=\"0xFFFFFF\" CLASS=\"NavBarCell3\"></TD>");
1101             }
1102         }
1103 
1104         reportFile.println("</TR>");
1105         reportFile.println("</TABLE>");
1106         reportFile.println("<HR>");
1107         reportFile.println("<!-- End of nav bar -->");
1108 */
1109     }
1110 
1111     /** Write the start of a table. */
writeTableStart(String title, int colSpan)1112     public void writeTableStart(String title, int colSpan) {
1113         reportFile.println("<p>");
1114         // Assumes that the first word of the title categorizes the table type
1115         // and that there is a space after the first word in the title
1116         int idx = title.indexOf(' ');
1117         String namedAnchor = title.substring(0, idx);
1118         reportFile.println("<a NAME=\"" + namedAnchor + "\"></a>"); // Named anchor
1119         reportFile.println("<TABLE summary=\"" + title+ "\" WIDTH=\"100%\">");
1120         reportFile.println("<TR>");
1121         reportFile.print("  <TH VALIGN=\"TOP\" COLSPAN=" + colSpan + ">");
1122         reportFile.println(title + "</FONT></TD>");
1123         reportFile.println("</TH>");
1124     }
1125 
1126     /**
1127      * If a class or package name is considered to be too long for convenient
1128      * display, insert <br> in the middle of it at a period.
1129      */
makeTwoRows(String name)1130     public String makeTwoRows(String name) {
1131         if (name.length() < 30)
1132             return name;
1133         int idx = name.indexOf(".", 20);
1134         if (idx == -1)
1135             return name;
1136         int len = name.length();
1137         String res = name.substring(0, idx+1) + "<br>" + name.substring(idx+1, len);
1138         return res;
1139     }
1140 
1141     /**
1142      * Write a table entry for a package, with support for links to Javadoc
1143      * for removed packages.
1144      *
1145      * linkType: 0 - no link by default, 1 = link to Javadoc HTML file, 2 = link to JDiff file
1146      */
writePackageTableEntry(String pkgName, int linkType, String possibleComment, boolean useOld)1147     public void writePackageTableEntry(String pkgName, int linkType,
1148                                        String possibleComment, boolean useOld) {
1149         if (!useOld) {
1150             reportFile.println("<TR BGCOLOR=\"" + bgcolor + "\" CLASS=\"TableRowColor\">");
1151             reportFile.println("  <TD VALIGN=\"TOP\" WIDTH=\"25%\">");
1152             reportFile.println("  <A NAME=\"" + pkgName + "\"></A>"); // Named anchor
1153         }
1154         //String shownPkgName = makeTwoRows(pkgName);
1155         if (linkType == 0) {
1156             if (oldDocPrefix == null) {
1157                 // No link
1158                 reportFile.print("  " + pkgName);
1159             } else {
1160                 // Call this method again but this time to emit a link to the
1161                 // old program element.
1162                 writePackageTableEntry(pkgName, 1, possibleComment, true);
1163             }
1164         } else if (linkType == 1) {
1165             // Link to HTML file for the package
1166             String pkgRef = pkgName;
1167             pkgRef = pkgRef.replace('.', '/');
1168             if (useOld)
1169                 pkgRef = oldDocPrefix + pkgRef + "/package-summary";
1170             else
1171                 pkgRef = newDocPrefix + pkgRef + "/package-summary";
1172             reportFile.println("  <nobr><A HREF=\"" + pkgRef + ".html\" target=\"_top\"><font size=\"+1\"><tt>" + pkgName + "</tt></font></A></nobr>");
1173         } else if (linkType == 2) {
1174             reportFile.println("  <nobr><A HREF=\"pkg_" + pkgName + reportFileExt + "\">" + pkgName + "</A></nobr>");
1175         }
1176         if (!useOld) {
1177             reportFile.println("  </TD>");
1178             emitComment(pkgName, possibleComment, linkType);
1179             reportFile.println("</TR>");
1180         }
1181     }
1182 
1183     /**
1184      * Write a table entry for a class or interface.
1185      *
1186      * linkType: 0 - no link by default, 1 = link to Javadoc HTML file, 2 = link to JDiff file
1187      */
writeClassTableEntry(String pkgName, String className, int linkType, boolean isInterface, String possibleComment, boolean useOld)1188     public void writeClassTableEntry(String pkgName, String className,
1189                                      int linkType, boolean isInterface,
1190                                      String possibleComment, boolean useOld) {
1191         if (!useOld) {
1192             reportFile.println("<TR BGCOLOR=\"" + bgcolor + "\" CLASS=\"TableRowColor\">");
1193             reportFile.println("  <TD VALIGN=\"TOP\" WIDTH=\"25%\">");
1194             reportFile.println("  <A NAME=\"" + className + "\"></A>"); // Named anchor
1195         }
1196         String fqName = pkgName + "." + className;
1197         String shownClassName = makeTwoRows(className);
1198         if (linkType == 0) {
1199             if (oldDocPrefix == null) {
1200                 // No link
1201                 if (isInterface)
1202                     reportFile.println("  <I>" + shownClassName + "</I>");
1203                 else
1204                     reportFile.println("  " + shownClassName);
1205             } else {
1206                 writeClassTableEntry(pkgName, className,
1207                                      1, isInterface,
1208                                      possibleComment, true);
1209             }
1210         } else if (linkType == 1) {
1211             // Link to HTML file for the class
1212             String classRef = fqName;
1213             // Deal with inner classes
1214             if (className.indexOf('.') != -1) {
1215                 classRef = pkgName + ".";
1216                 classRef = classRef.replace('.', '/');
1217                 if (useOld)
1218                     classRef = oldDocPrefix + classRef + className;
1219                 else
1220                     classRef = newDocPrefix + classRef + className;
1221             } else {
1222                 classRef = classRef.replace('.', '/');
1223                 if (useOld)
1224                     classRef = oldDocPrefix + classRef;
1225                 else
1226                     classRef = newDocPrefix + classRef;
1227             }
1228             reportFile.print("  <nobr><A HREF=\"" + classRef + ".html\" target=\"_top\"><font size=\"+1\"><tt>");
1229             if (isInterface)
1230                 reportFile.print("<I>" + shownClassName + "</I>");
1231             else
1232                 reportFile.print(shownClassName);
1233             reportFile.println("</tt></font></A></nobr>");
1234         } else if (linkType == 2) {
1235             reportFile.print("  <nobr><A HREF=\"" + fqName + reportFileExt + "\">");
1236             if (isInterface)
1237                 reportFile.print("<I>" + shownClassName + "</I>");
1238             else
1239                 reportFile.print(shownClassName);
1240             reportFile.println("</A></nobr>");
1241         }
1242         if (!useOld) {
1243             reportFile.println("  </TD>");
1244             emitComment(fqName, possibleComment, linkType);
1245             reportFile.println("</TR>");
1246         }
1247     }
1248 
1249     /**
1250      * Write a table entry for a constructor.
1251      *
1252      * linkType: 0 - no link by default, 1 = link to Javadoc HTML file
1253      */
writeCtorTableEntry(String pkgName, String className, String type, int linkType, String possibleComment, boolean useOld)1254     public void writeCtorTableEntry(String pkgName, String className,
1255                                     String type, int linkType,
1256                                     String possibleComment, boolean useOld) {
1257         String fqName = pkgName + "." + className;
1258         String shownClassName = makeTwoRows(className);
1259         String lt = "removed";
1260         if (linkType ==1)
1261             lt = "added";
1262         String commentID = fqName + ".ctor_" + lt + "(" + type + ")";
1263         if (!useOld) {
1264             reportFile.println("<TR BGCOLOR=\"" + bgcolor + "\" CLASS=\"TableRowColor\">");
1265             reportFile.println("  <TD VALIGN=\"TOP\" WIDTH=\"25%\">");
1266             reportFile.println("  <A NAME=\"" + commentID + "\"></A>"); // Named anchor
1267         }
1268         String shortType = simpleName(type);
1269         if (linkType == 0) {
1270             if (oldDocPrefix == null) {
1271                 // No link
1272                 reportFile.print("  <nobr>" + pkgName);
1273                 emitTypeWithParens(shortType);
1274                 reportFile.println("</nobr>");
1275             } else {
1276                 writeCtorTableEntry(pkgName, className,
1277                                     type, 1,
1278                                     possibleComment, true);
1279             }
1280         } else if (linkType == 1) {
1281             // Link to HTML file for the package
1282             String memberRef = fqName.replace('.', '/');
1283             // Deal with inner classes
1284             if (className.indexOf('.') != -1) {
1285                 memberRef = pkgName + ".";
1286                 memberRef = memberRef.replace('.', '/');
1287                 if (useOld) {
1288                     // oldDocPrefix is non-null at this point
1289                     memberRef = oldDocPrefix + memberRef + className;
1290                 } else {
1291                     memberRef = newDocPrefix + memberRef + className;
1292                 }
1293             } else {
1294                 if (useOld) {
1295                     // oldDocPrefix is non-null at this point
1296                     memberRef = oldDocPrefix + memberRef;
1297                 } else {
1298                     memberRef = newDocPrefix + memberRef;
1299                 }
1300             }
1301             reportFile.print("  <nobr><A HREF=\"" + memberRef + ".html#" + className +
1302                              "(" + type + ")\" target=\"_top\"><font size=\"+1\"><tt>" + shownClassName + "</tt></font></A>");
1303             emitTypeWithParens(shortType);
1304             reportFile.println("</nobr>");
1305         }
1306         if (!useOld) {
1307             reportFile.println("  </TD>");
1308             emitComment(commentID, possibleComment, linkType);
1309             reportFile.println("</TR>");
1310         }
1311     }
1312 
1313     /**
1314      * Write a table entry for a changed constructor.
1315      */
writeCtorChangedTableEntry(String pkgName, String className, MemberDiff memberDiff)1316     public void writeCtorChangedTableEntry(String pkgName, String className,
1317                                            MemberDiff memberDiff) {
1318         String fqName = pkgName + "." + className;
1319         String newSignature = memberDiff.newType_;
1320         if (newSignature.compareTo("void") == 0)
1321             newSignature = "";
1322         String commentID = fqName + ".ctor_changed(" + newSignature + ")";
1323         reportFile.println("<TR BGCOLOR=\"" + bgcolor + "\" CLASS=\"TableRowColor\">");
1324         reportFile.println("  <TD VALIGN=\"TOP\" WIDTH=\"25%\">");
1325         reportFile.println("  <A NAME=\"" + commentID + "\"></A>"); // Named anchor
1326         String memberRef = fqName.replace('.', '/');
1327         String shownClassName = makeTwoRows(className);
1328         // Deal with inner classes
1329         if (className.indexOf('.') != -1) {
1330             memberRef = pkgName + ".";
1331             memberRef = memberRef.replace('.', '/');
1332             memberRef = newDocPrefix + memberRef + className;
1333         } else {
1334             memberRef = newDocPrefix + memberRef;
1335         }
1336         String newType = memberDiff.newType_;
1337         if (newType.compareTo("void") == 0)
1338             newType = "";
1339         String shortNewType = simpleName(memberDiff.newType_);
1340         // Constructors have the linked name, then the type in parentheses.
1341         reportFile.print("  <nobr><A HREF=\"" + memberRef + ".html#" + className + "(" + newType + ")\" target=\"_top\"><font size=\"+1\"><tt>");
1342         reportFile.print(shownClassName);
1343         reportFile.print("</tt></font></A>");
1344         emitTypeWithParens(shortNewType);
1345         reportFile.println("  </nobr>");
1346         reportFile.println("  </TD>");
1347 
1348         // Report changes in documentation
1349         if (reportDocChanges && memberDiff.documentationChange_ != null) {
1350             String oldMemberRef = null;
1351             String oldType = null;
1352             if (oldDocPrefix != null) {
1353                 oldMemberRef = pkgName + "." + className;
1354                 oldMemberRef = oldMemberRef.replace('.', '/');
1355                 if (className.indexOf('.') != -1) {
1356                     oldMemberRef = pkgName + ".";
1357                     oldMemberRef = oldMemberRef.replace('.', '/');
1358                     oldMemberRef = oldDocPrefix + oldMemberRef + className;
1359                 } else {
1360                     oldMemberRef = oldDocPrefix + oldMemberRef;
1361                 }
1362                 oldType = memberDiff.oldType_;
1363                 if (oldType.compareTo("void") == 0)
1364                     oldType = "";
1365             }
1366             if (oldDocPrefix != null)
1367                 memberDiff.documentationChange_ += "<A HREF=\"" +
1368                     oldMemberRef + ".html#" + className + "(" + oldType +
1369                     ")\" target=\"_self\"><font size=\"+1\"><tt>old</tt></font></A> to ";
1370             else
1371                 memberDiff.documentationChange_ += "<font size=\"+1\"><tt>old</tt></font> to ";
1372             memberDiff.documentationChange_ += "<A HREF=\"" + memberRef +
1373                 ".html#" + className + "(" + newType +
1374                 ")\" target=\"_self\"><font size=\"+1\"><tt>new</tt></font></A>.<br>";
1375         }
1376 
1377         emitChanges(memberDiff, 0);
1378         emitComment(commentID, null, 2);
1379 
1380         reportFile.println("</TR>");
1381     }
1382 
1383     /**
1384      * Write a table entry for a method.
1385      *
1386      * linkType: 0 - no link by default, 1 = link to Javadoc HTML file
1387      */
writeMethodTableEntry(String pkgName, String className, MethodAPI methodAPI, int linkType, String possibleComment, boolean useOld)1388     public void writeMethodTableEntry(String pkgName, String className,
1389                                       MethodAPI methodAPI, int linkType,
1390                                       String possibleComment, boolean useOld) {
1391         String fqName = pkgName + "." + className;
1392         String signature = methodAPI.getSignature();
1393         String methodName = methodAPI.name_;
1394         String lt = "removed";
1395         if (linkType ==1)
1396             lt = "added";
1397         String commentID = fqName + "." + methodName + "_" + lt + "(" + signature + ")";
1398         if (!useOld) {
1399             reportFile.println("<TR BGCOLOR=\"" + bgcolor + "\" CLASS=\"TableRowColor\">");
1400             reportFile.println("  <TD VALIGN=\"TOP\" WIDTH=\"25%\">");
1401             reportFile.println("  <A NAME=\"" + commentID + "\"></A>"); // Named anchor
1402         }
1403         if (signature.compareTo("void") == 0)
1404             signature = "";
1405         String shortSignature = simpleName(signature);
1406         String returnType = methodAPI.returnType_;
1407         String shortReturnType = simpleName(returnType);
1408         if (linkType == 0) {
1409             if (oldDocPrefix == null) {
1410                 // No link
1411                 reportFile.print("  <nobr>");
1412                 emitType(shortReturnType);
1413                 reportFile.print("&nbsp;" + methodName);
1414                 emitTypeWithParens(shortSignature);
1415                 reportFile.println("</nobr>");
1416             } else {
1417                 writeMethodTableEntry(pkgName, className,
1418                                       methodAPI, 1,
1419                                       possibleComment, true);
1420             }
1421         } else if (linkType == 1) {
1422             // Link to HTML file for the package
1423             String memberRef = fqName.replace('.', '/');
1424             // Deal with inner classes
1425             if (className.indexOf('.') != -1) {
1426                 memberRef = pkgName + ".";
1427                 memberRef = memberRef.replace('.', '/');
1428                 if (useOld) {
1429                     // oldDocPrefix is non-null at this point
1430                     memberRef = oldDocPrefix + memberRef + className;
1431                 } else {
1432                     memberRef = newDocPrefix + memberRef + className;
1433                 }
1434             } else {
1435                 if (useOld) {
1436                     // oldDocPrefix is non-null at this point
1437                     memberRef = oldDocPrefix + memberRef;
1438                 } else {
1439                     memberRef = newDocPrefix + memberRef;
1440                 }
1441             }
1442             reportFile.print("  <nobr>");
1443             emitType(shortReturnType);
1444             reportFile.print("&nbsp;<A HREF=\"" + memberRef + ".html#" + methodName +
1445                "(" + signature + ")\" target=\"_top\"><font size=\"+1\"><tt>" + methodName + "</tt></font></A>");
1446             emitTypeWithParens(shortSignature);
1447             reportFile.println("</nobr>");
1448         }
1449         if (!useOld) {
1450             reportFile.println("  </TD>");
1451             emitComment(commentID, possibleComment, linkType);
1452             reportFile.println("</TR>");
1453         }
1454     }
1455 
1456     /**
1457      * Write a table entry for a changed method.
1458      */
writeMethodChangedTableEntry(String pkgName, String className, MemberDiff memberDiff)1459     public void writeMethodChangedTableEntry(String pkgName, String className,
1460                                       MemberDiff memberDiff) {
1461         String memberName = memberDiff.name_;
1462         // Generally nowhere to break a member name anyway
1463         // String shownMemberName = makeTwoRows(memberName);
1464         String fqName = pkgName + "." + className;
1465         String newSignature = memberDiff.newSignature_;
1466         String commentID = fqName + "." + memberName + "_changed(" + newSignature + ")";
1467         reportFile.println("<TR BGCOLOR=\"" + bgcolor + "\" CLASS=\"TableRowColor\">");
1468 
1469         reportFile.println("  <TD VALIGN=\"TOP\" WIDTH=\"25%\">");
1470         reportFile.println("  <A NAME=\"" + commentID + "\"></A>"); // Named anchor
1471         String memberRef = fqName.replace('.', '/');
1472         // Deal with inner classes
1473         if (className.indexOf('.') != -1) {
1474             memberRef = pkgName + ".";
1475             memberRef = memberRef.replace('.', '/');
1476             memberRef = newDocPrefix + memberRef + className;
1477         } else {
1478             memberRef = newDocPrefix + memberRef;
1479         }
1480         // Javadoc generated HTML has no named anchors for methods
1481         // inherited from other classes, so link to the defining class' method.
1482         // Only copes with non-inner classes.
1483         if (className.indexOf('.') == -1 &&
1484             memberDiff.modifiersChange_ != null &&
1485             memberDiff.modifiersChange_.indexOf("but is now inherited from") != -1) {
1486             memberRef = memberDiff.inheritedFrom_;
1487             memberRef = memberRef.replace('.', '/');
1488             memberRef = newDocPrefix + memberRef;
1489         }
1490 
1491         String newReturnType = memberDiff.newType_;
1492         String shortReturnType = simpleName(newReturnType);
1493         String shortSignature = simpleName(newSignature);
1494         reportFile.print("  <nobr>");
1495         emitTypeWithNoParens(shortReturnType);
1496         reportFile.print("&nbsp;<A HREF=\"" + memberRef + ".html#" +
1497                          memberName + "(" + newSignature + ")\" target=\"_top\"><font size=\"+1\"><tt>");
1498         reportFile.print(memberName);
1499         reportFile.print("</tt></font></A>");
1500         emitTypeWithParens(shortSignature);
1501         reportFile.println("  </nobr>");
1502         reportFile.println("  </TD>");
1503 
1504         // Report changes in documentation
1505         if (reportDocChanges && memberDiff.documentationChange_ != null) {
1506             String oldMemberRef = null;
1507             String oldSignature = null;
1508             if (oldDocPrefix != null) {
1509                 oldMemberRef = pkgName + "." + className;
1510                 oldMemberRef = oldMemberRef.replace('.', '/');
1511                 if (className.indexOf('.') != -1) {
1512                     oldMemberRef = pkgName + ".";
1513                     oldMemberRef = oldMemberRef.replace('.', '/');
1514                     oldMemberRef = oldDocPrefix + oldMemberRef + className;
1515                 } else {
1516                     oldMemberRef = oldDocPrefix + oldMemberRef;
1517                 }
1518                 oldSignature = memberDiff.oldSignature_;
1519             }
1520             if (oldDocPrefix != null)
1521                 memberDiff.documentationChange_ += "<A HREF=\"" +
1522                     oldMemberRef + ".html#" + memberName + "(" +
1523                     oldSignature + ")\" target=\"_self\"><font size=\"+1\"><tt>old</tt></font></A> to ";
1524             else
1525                 memberDiff.documentationChange_ += "<font size=\"+1\"><tt>old</tt></font> to ";
1526             memberDiff.documentationChange_ += "<A HREF=\"" + memberRef +
1527                 ".html#" + memberName + "(" + newSignature +
1528                 ")\" target=\"_self\"><font size=\"+1\"><tt>new</tt></font></A>.<br>";
1529         }
1530 
1531         emitChanges(memberDiff, 1);
1532         // Get the comment from the parent class if more appropriate
1533         if (memberDiff.modifiersChange_ != null) {
1534             int parentIdx = memberDiff.modifiersChange_.indexOf("now inherited from");
1535             if (parentIdx != -1) {
1536                 // Change the commentID to pick up the appropriate method
1537                 commentID = memberDiff.inheritedFrom_ + "." + memberName +
1538                     "_changed(" + newSignature + ")";
1539             }
1540         }
1541         emitComment(commentID, null, 2);
1542 
1543         reportFile.println("</TR>");
1544     }
1545 
1546     /**
1547      * Write a table entry for a field.
1548      *
1549      * linkType: 0 - no link by default, 1 = link to Javadoc HTML file
1550      */
writeFieldTableEntry(String pkgName, String className, FieldAPI fieldAPI, int linkType, String possibleComment, boolean useOld)1551     public void writeFieldTableEntry(String pkgName, String className,
1552                                      FieldAPI fieldAPI, int linkType,
1553                                      String possibleComment, boolean useOld) {
1554         String fqName = pkgName + "." + className;
1555         // Fields can only appear in one table, so no need to specify _added etc
1556         String fieldName = fieldAPI.name_;
1557         // Generally nowhere to break a member name anyway
1558         // String shownFieldName = makeTwoRows(fieldName);
1559         String commentID = fqName + "." + fieldName;
1560         if (!useOld) {
1561             reportFile.println("<TR BGCOLOR=\"" + bgcolor + "\" CLASS=\"TableRowColor\">");
1562             reportFile.println("  <TD VALIGN=\"TOP\" WIDTH=\"25%\">");
1563             reportFile.println("  <A NAME=\"" + commentID + "\"></A>"); // Named anchor
1564         }
1565         String fieldType = fieldAPI.type_;
1566         if (fieldType.compareTo("void") == 0)
1567             fieldType = "";
1568         String shortFieldType = simpleName(fieldType);
1569         if (linkType == 0) {
1570             if (oldDocPrefix == null) {
1571                 // No link.
1572                 reportFile.print("  ");
1573                 emitType(shortFieldType);
1574                 reportFile.println("&nbsp;" + fieldName);
1575             } else {
1576                 writeFieldTableEntry(pkgName, className,
1577                                      fieldAPI, 1,
1578                                      possibleComment, true);
1579             }
1580         } else if (linkType == 1) {
1581             // Link to HTML file for the package.
1582             String memberRef = fqName.replace('.', '/');
1583             // Deal with inner classes
1584             if (className.indexOf('.') != -1) {
1585                 memberRef = pkgName + ".";
1586                 memberRef = memberRef.replace('.', '/');
1587                 if (useOld)
1588                     memberRef = oldDocPrefix + memberRef + className;
1589                 else
1590                     memberRef = newDocPrefix + memberRef + className;
1591             } else {
1592                 if (useOld)
1593                     memberRef = oldDocPrefix + memberRef;
1594                 else
1595                     memberRef = newDocPrefix + memberRef;
1596             }
1597             reportFile.print("  <nobr>");
1598             emitType(shortFieldType);
1599             reportFile.println("&nbsp;<A HREF=\"" + memberRef + ".html#" + fieldName +
1600                "\" target=\"_top\"><font size=\"+1\"><tt>" + fieldName + "</tt></font></A></nobr>");
1601         }
1602         if (!useOld) {
1603             reportFile.println("  </TD>");
1604             emitComment(commentID, possibleComment, linkType);
1605             reportFile.println("</TR>");
1606         }
1607         }
1608 
1609     /**
1610      * Write a table entry for a changed field.
1611      */
writeFieldChangedTableEntry(String pkgName, String className, MemberDiff memberDiff)1612     public void writeFieldChangedTableEntry(String pkgName, String className,
1613                                             MemberDiff memberDiff) {
1614         String memberName = memberDiff.name_;
1615         // Generally nowhere to break a member name anyway
1616         // String shownMemberName = makeTwoRows(memberName);
1617         String fqName = pkgName + "." + className;
1618         // Fields have unique names in a class
1619         String commentID = fqName + "." + memberName;
1620         reportFile.println("<TR BGCOLOR=\"" + bgcolor + "\" CLASS=\"TableRowColor\">");
1621 
1622         reportFile.println("  <TD VALIGN=\"TOP\" WIDTH=\"25%\">");
1623         reportFile.println("  <A NAME=\"" + commentID + "\"></A>"); // Named anchor
1624         String memberRef = fqName.replace('.', '/');
1625         // Deal with inner classes
1626         if (className.indexOf('.') != -1) {
1627             memberRef = pkgName + ".";
1628             memberRef = memberRef.replace('.', '/');
1629             memberRef = newDocPrefix + memberRef + className;
1630         } else {
1631             memberRef = newDocPrefix + memberRef;
1632         }
1633         // Javadoc generated HTML has no named anchors for fields
1634         // inherited from other classes, so link to the defining class' field.
1635         // Only copes with non-inner classes.
1636         if (className.indexOf('.') == -1 &&
1637             memberDiff.modifiersChange_ != null &&
1638             memberDiff.modifiersChange_.indexOf("but is now inherited from") != -1) {
1639             memberRef = memberDiff.inheritedFrom_;
1640             memberRef = memberRef.replace('.', '/');
1641             memberRef = newDocPrefix + memberRef;
1642         }
1643 
1644         String newType = memberDiff.newType_;
1645         String shortNewType = simpleName(newType);
1646         reportFile.print("  <nobr>");
1647         emitTypeWithNoParens(shortNewType);
1648         reportFile.print("&nbsp;<A HREF=\"" + memberRef + ".html#" +
1649                          memberName + "\" target=\"_top\"><font size=\"+1\"><tt>");
1650         reportFile.print(memberName);
1651         reportFile.print("</tt></font></A></nobr>");
1652         reportFile.println("  </TD>");
1653 
1654         // Report changes in documentation
1655         if (reportDocChanges && memberDiff.documentationChange_ != null) {
1656             String oldMemberRef = null;
1657             if (oldDocPrefix != null) {
1658                 oldMemberRef = pkgName + "." + className;
1659                 oldMemberRef = oldMemberRef.replace('.', '/');
1660                 if (className.indexOf('.') != -1) {
1661                     oldMemberRef = pkgName + ".";
1662                     oldMemberRef = oldMemberRef.replace('.', '/');
1663                     oldMemberRef = oldDocPrefix + oldMemberRef + className;
1664                 } else {
1665                     oldMemberRef = oldDocPrefix + oldMemberRef;
1666                 }
1667             }
1668             if (oldDocPrefix != null)
1669                 memberDiff.documentationChange_ += "<A HREF=\"" +
1670                     oldMemberRef + ".html#" + memberName + "\" target=\"_self\"><font size=\"+1\"><tt>old</tt></font></A> to ";
1671             else
1672                 memberDiff.documentationChange_ += "<font size=\"+1\"><tt>old</tt></font> to ";
1673             memberDiff.documentationChange_ += "<A HREF=\"" + memberRef +
1674                 ".html#" + memberName + "\" target=\"_self\"><font size=\"+1\"><tt>new</tt></font></A>.<br>";
1675         }
1676 
1677         emitChanges(memberDiff, 2);
1678         // Get the comment from the parent class if more appropriate
1679         if (memberDiff.modifiersChange_ != null) {
1680             int parentIdx = memberDiff.modifiersChange_.indexOf("now inherited from");
1681             if (parentIdx != -1) {
1682                 // Change the commentID to pick up the appropriate method
1683                 commentID = memberDiff.inheritedFrom_ + "." + memberName;
1684             }
1685         }
1686         emitComment(commentID, null, 2);
1687 
1688         reportFile.println("</TR>");
1689     }
1690 
1691     /**
1692      * Emit all changes associated with a MemberDiff as an entry in a table.
1693      *
1694      * @param memberType 0 = ctor, 1 = method, 2 = field
1695      */
emitChanges(MemberDiff memberDiff, int memberType)1696     public void emitChanges(MemberDiff memberDiff, int memberType){
1697         reportFile.println("  <TD VALIGN=\"TOP\" WIDTH=\"30%\">");
1698         boolean hasContent = false;
1699         // The type or return type changed
1700         if (memberDiff.oldType_.compareTo(memberDiff.newType_) != 0) {
1701             String shortOldType = simpleName(memberDiff.oldType_);
1702             String shortNewType = simpleName(memberDiff.newType_);
1703             if (memberType == 1) {
1704                 reportFile.print("Change in return type from ");
1705             } else {
1706                 reportFile.print("Change in type from ");
1707             }
1708             if (shortOldType.compareTo(shortNewType) == 0) {
1709                 // The types differ in package name, so use the full name
1710                 shortOldType = memberDiff.oldType_;
1711                 shortNewType = memberDiff.newType_;
1712             }
1713             emitType(shortOldType);
1714             reportFile.print(" to ");
1715             emitType(shortNewType);
1716             reportFile.println(".<br>");
1717             hasContent = true;
1718         }
1719         // The signatures changed - only used by methods
1720         if (memberType == 1 &&
1721             memberDiff.oldSignature_ != null &&
1722             memberDiff.newSignature_ != null &&
1723             memberDiff.oldSignature_.compareTo(memberDiff.newSignature_) != 0) {
1724             String shortOldSignature = simpleName(memberDiff.oldSignature_);
1725             String shortNewSignature = simpleName(memberDiff.newSignature_);
1726             if (shortOldSignature.compareTo(shortNewSignature) == 0) {
1727                 // The signatures differ in package names, so use the full form
1728                 shortOldSignature = memberDiff.oldSignature_;
1729                 shortNewSignature = memberDiff.newSignature_;
1730             }
1731             if (hasContent)
1732                 reportFile.print(" ");
1733             reportFile.print("Change in signature from ");
1734             if (shortOldSignature.compareTo("") == 0)
1735                 shortOldSignature = "void";
1736             emitType(shortOldSignature);
1737             reportFile.print(" to ");
1738             if (shortNewSignature.compareTo("") == 0)
1739                 shortNewSignature = "void";
1740             emitType(shortNewSignature);
1741             reportFile.println(".<br>");
1742             hasContent = true;
1743         }
1744         // The exceptions are only non-null in methods and constructors
1745         if (memberType != 2 &&
1746             memberDiff.oldExceptions_ != null &&
1747             memberDiff.newExceptions_ != null &&
1748             memberDiff.oldExceptions_.compareTo(memberDiff.newExceptions_) != 0) {
1749             if (hasContent)
1750                 reportFile.print(" ");
1751             // If either one of the exceptions has no spaces in it, or is
1752             // equal to "no exceptions", then just display the whole
1753             // exceptions texts.
1754             int spaceInOld = memberDiff.oldExceptions_.indexOf(" ");
1755             if (memberDiff.oldExceptions_.compareTo("no exceptions") == 0)
1756                 spaceInOld = -1;
1757             int spaceInNew = memberDiff.newExceptions_.indexOf(" ");
1758             if (memberDiff.newExceptions_.compareTo("no exceptions") == 0)
1759                 spaceInNew = -1;
1760             if (spaceInOld == -1 || spaceInNew == -1) {
1761                 reportFile.print("Change in exceptions thrown from ");
1762                 emitException(memberDiff.oldExceptions_);
1763                 reportFile.print(" to " );
1764                 emitException(memberDiff.newExceptions_);
1765                 reportFile.println(".<br>");
1766             } else {
1767                 // Too many exceptions become unreadable, so just show the
1768                 // individual changes. Catch the case where exceptions are
1769                 // just reordered.
1770                 boolean firstChange = true;
1771                 int numRemoved = 0;
1772                 StringTokenizer stOld = new StringTokenizer(memberDiff.oldExceptions_, ", ");
1773                 while (stOld.hasMoreTokens()) {
1774                     String oldException = stOld.nextToken();
1775                     if (!memberDiff.newExceptions_.startsWith(oldException) &&
1776                         !(memberDiff.newExceptions_.indexOf(", " + oldException) != -1)) {
1777                         if (firstChange) {
1778                             reportFile.print("Change in exceptions: ");
1779                             firstChange = false;
1780                         }
1781                         if (numRemoved != 0)
1782                             reportFile.print(", ");
1783                         emitException(oldException);
1784                         numRemoved++;
1785                     }
1786                 }
1787                 if (numRemoved == 1)
1788                     reportFile.print(" was removed.");
1789                 else if (numRemoved > 1)
1790                     reportFile.print(" were removed.");
1791 
1792                 int numAdded = 0;
1793                 StringTokenizer stNew = new StringTokenizer(memberDiff.newExceptions_, ", ");
1794                 while (stNew.hasMoreTokens()) {
1795                     String newException = stNew.nextToken();
1796                     if (!memberDiff.oldExceptions_.startsWith(newException) &&
1797                         !(memberDiff.oldExceptions_.indexOf(", " + newException) != -1)) {
1798                         if (firstChange) {
1799                             reportFile.print("Change in exceptions: ");
1800                             firstChange = false;
1801                         }
1802                         if (numAdded != 0)
1803                             reportFile.println(", ");
1804                         else
1805                             reportFile.println(" ");
1806                         emitException(newException);
1807                         numAdded++;
1808                     }
1809                 }
1810                 if (numAdded == 1)
1811                     reportFile.print(" was added");
1812                 else if (numAdded > 1)
1813                     reportFile.print(" were added");
1814                 else if (numAdded == 0 && numRemoved == 0 && firstChange)
1815                     reportFile.print("Exceptions were reordered");
1816                 reportFile.println(".<br>");
1817             }
1818             // Note the changes between a comma-separated list of Strings
1819             hasContent = true;
1820         }
1821 
1822         if (memberDiff.documentationChange_ != null) {
1823             if (hasContent)
1824                 reportFile.print(" ");
1825             reportFile.print(memberDiff.documentationChange_);
1826             hasContent = true;
1827         }
1828 
1829         // Last, so no need for a <br>
1830         if (memberDiff.modifiersChange_ != null) {
1831             if (hasContent)
1832                 reportFile.print(" ");
1833             reportFile.println(memberDiff.modifiersChange_);
1834             hasContent = true;
1835         }
1836         reportFile.println("  </TD>");
1837     }
1838 
1839     /**
1840      * Emit a string which is an exception by surrounding it with
1841      * &lt;code&gt; tags.
1842      * If there is a space in the type, e.g. &quot;String, File&quot;, then
1843      * surround it with parentheses too. Do not add &lt;code&gt; tags or
1844      * parentheses if the String is "no exceptions".
1845      */
emitException(String ex)1846     public void emitException(String ex) {
1847         if (ex.compareTo("no exceptions") == 0) {
1848             reportFile.print(ex);
1849         } else {
1850             if (ex.indexOf(' ') != -1) {
1851                 reportFile.print("(<code>" + ex + "</code>)");
1852             } else {
1853                 reportFile.print("<code>" + ex + "</code>");
1854             }
1855         }
1856     }
1857 
1858     /**
1859      * Emit a string which is a type by surrounding it with &lt;code&gt; tags.
1860      * If there is a space in the type, e.g. &quot;String, File&quot;, then
1861      * surround it with parentheses too.
1862      */
emitType(String type)1863     public void emitType(String type) {
1864         if (type.compareTo("") == 0)
1865             return;
1866         if (type.indexOf(' ') != -1) {
1867             reportFile.print("(<code>" + type + "</code>)");
1868         } else {
1869             reportFile.print("<code>" + type + "</code>");
1870         }
1871     }
1872 
1873     /**
1874      * Emit a string which is a type by surrounding it with &lt;code&gt; tags.
1875      * Also surround it with parentheses too. Used to display methods'
1876      * parameters.
1877      * Suggestions for where a browser should break the
1878      * text are provided with &lt;br> and &ltnobr> tags.
1879      */
emitTypeWithParens(String type)1880     public static void emitTypeWithParens(String type) {
1881         emitTypeWithParens(type, true);
1882     }
1883 
1884     /**
1885      * Emit a string which is a type by surrounding it with &lt;code&gt; tags.
1886      * Also surround it with parentheses too. Used to display methods'
1887      * parameters.
1888      */
emitTypeWithParens(String type, boolean addBreaks)1889     public static void emitTypeWithParens(String type, boolean addBreaks) {
1890         if (type.compareTo("") == 0)
1891             reportFile.print("()");
1892         else {
1893             int idx = type.indexOf(", ");
1894             if (!addBreaks || idx == -1) {
1895                 reportFile.print("(<code>" + type + "</code>)");
1896             } else {
1897                 // Make the browser break text at reasonable places
1898                 String sepType = null;
1899                 StringTokenizer st = new StringTokenizer(type, ", ");
1900                 while (st.hasMoreTokens()) {
1901                     String p = st.nextToken();
1902                     if (sepType == null)
1903                         sepType = p;
1904                     else
1905                         sepType += ",</nobr> " + p + "<nobr>";
1906                 }
1907                 reportFile.print("(<code>" + sepType + "<nobr></code>)");
1908             }
1909         }
1910     }
1911 
1912     /**
1913      * Emit a string which is a type by surrounding it with &lt;code&gt; tags.
1914      * Do not surround it with parentheses. Used to display methods' return
1915      * types and field types.
1916      */
emitTypeWithNoParens(String type)1917     public static void emitTypeWithNoParens(String type) {
1918         if (type.compareTo("") != 0)
1919             reportFile.print("<code>" + type + "</code>");
1920     }
1921 
1922     /**
1923      * Return a String with the simple names of the classes in fqName.
1924      * &quot;java.lang.String&quot; becomes &quot;String&quot;,
1925      * &quotjava.lang.String, java.io.File&quot becomes &quotString, File&quot;
1926      * and so on. If fqName is null, return null. If fqName is &quot;&quot;,
1927      * return &quot;&quot;.
1928      */
simpleName(String fqNames)1929     public static String simpleName(String fqNames) {
1930         if (fqNames == null)
1931             return null;
1932         String res = "";
1933         boolean hasContent = false;
1934         // We parse the string step by step to ensure we take
1935         // fqNames that contains generics parameter in a whole.
1936         ArrayList<String> fqNamesList = new ArrayList<String>();
1937         int genericParametersDepth = 0;
1938         StringBuffer buffer = new StringBuffer();
1939         for (int i=0; i<fqNames.length(); i++) {
1940           char c = fqNames.charAt(i);
1941           if ('<' == c) {
1942             genericParametersDepth++;
1943           }
1944           if ('>' == c) {
1945             genericParametersDepth--;
1946           }
1947           if (',' != c || genericParametersDepth > 0) {
1948             buffer.append(c);
1949           } else if (',' == c) {
1950             fqNamesList.add(buffer.toString().trim());
1951             buffer = new StringBuffer(buffer.length());
1952           }
1953         }
1954         fqNamesList.add(buffer.toString().trim());
1955         for (String fqName : fqNamesList) {
1956             // Assume this will be used inside a <nobr> </nobr> set of tags.
1957             if (hasContent)
1958                 res += ", ";
1959             hasContent = true;
1960             // Look for text within '<' and '>' in case this is a invocation of a generic
1961 
1962             int firstBracket = fqName.indexOf('<');
1963             int lastBracket = fqName.lastIndexOf('>');
1964             String genericParameter = null;
1965             if (firstBracket != -1 && lastBracket != -1) {
1966               genericParameter = simpleName(fqName.substring(firstBracket + 1, lastBracket));
1967               fqName = fqName.substring(0, firstBracket);
1968             }
1969 
1970             int lastDot = fqName.lastIndexOf('.');
1971             if (lastDot < 0) {
1972                 res += fqName; // Already as simple as possible
1973             } else {
1974                 res += fqName.substring(lastDot+1);
1975             }
1976             if (genericParameter != null)
1977               res += "&lt;" + genericParameter + "&gt;";
1978         }
1979         return res;
1980     }
1981 
1982     /**
1983      * Find any existing comment and emit it. Add the new comment to the
1984      * list of new comments. The first instance of the string "@first" in
1985      * a hand-written comment will be replaced by the first sentence from
1986      * the associated doc block, if such exists. Also replace @link by
1987      * an HTML link.
1988      *
1989      * @param commentID The identifier for this comment.
1990      * @param possibleComment A possible comment from another source.
1991      * @param linkType 0 = remove, 1 = add, 2 = change
1992      */
emitComment(String commentID, String possibleComment, int linkType)1993     public void emitComment(String commentID, String possibleComment,
1994                             int linkType) {
1995         if (noCommentsOnRemovals && linkType == 0) {
1996             reportFile.println("  <TD>&nbsp;</TD>");
1997             return;
1998         }
1999         if (noCommentsOnAdditions && linkType == 1) {
2000             reportFile.println("  <TD>&nbsp;</TD>");
2001             return;
2002         }
2003         if (noCommentsOnChanges && linkType == 2) {
2004             reportFile.println("  <TD>&nbsp;</TD>");
2005             return;
2006         }
2007 
2008         // We have to use this global hash table because the *Diff classes
2009         // do not store the possible comment from the new *API object.
2010         if (!noCommentsOnChanges && possibleComment == null) {
2011             possibleComment = (String)Comments.allPossibleComments.get(commentID);
2012         }
2013         // Just use the first sentence of the possible comment.
2014         if (possibleComment != null) {
2015             int fsidx = RootDocToXML.endOfFirstSentence(possibleComment, false);
2016             if (fsidx != -1 && fsidx != 0)
2017                 possibleComment = possibleComment.substring(0, fsidx+1);
2018         }
2019 
2020         String comment = Comments.getComment(existingComments_, commentID);
2021         if (comment.compareTo(Comments.placeHolderText) == 0) {
2022             if (possibleComment != null &&
2023                 possibleComment.indexOf("InsertOtherCommentsHere") == -1)
2024                 reportFile.println("  <TD VALIGN=\"TOP\">" + possibleComment + "</TD>");
2025             else
2026                 reportFile.println("  <TD>&nbsp;</TD>");
2027         } else {
2028             int idx = comment.indexOf("@first");
2029             if (idx == -1) {
2030                 reportFile.println("  <TD VALIGN=\"TOP\">" + Comments.convertAtLinks(comment, "", null, null) + "</TD>");
2031             } else {
2032                 reportFile.print("  <TD VALIGN=\"TOP\">" + comment.substring(0, idx));
2033                 if (possibleComment != null &&
2034                     possibleComment.indexOf("InsertOtherCommentsHere") == -1)
2035                     reportFile.print(possibleComment);
2036                 reportFile.println(comment.substring(idx + 6) + "</TD>");
2037             }
2038         }
2039         SingleComment newComment = new SingleComment(commentID, comment);
2040         newComments_.addComment(newComment);
2041     }
2042 
2043     /** Write the end of a table. */
writeTableEnd()2044     public void writeTableEnd() {
2045         reportFile.println("</TABLE>");
2046         reportFile.println("&nbsp;");
2047     }
2048 
2049     /** Write a newline out. */
writeText()2050     public void writeText() {
2051         reportFile.println();
2052     }
2053 
2054     /** Write some text out. */
writeText(String text)2055     public void writeText(String text) {
2056         reportFile.println(text);
2057     }
2058 
2059     /** Emit some non-breaking space for indentation. */
indent(int indent)2060     public void indent(int indent) {
2061         for (int i = 0; i < indent; i++)
2062             reportFile.print("&nbsp;");
2063     }
2064 
2065     /**
2066      * The name of the file to which the top-level HTML file is written,
2067      * and also the name of the subdirectory where most of the HTML appears,
2068      * and also a prefix for the names of some of the files in that
2069      * subdirectory.
2070      */
2071     static String reportFileName = "changes";
2072 
2073     /**
2074      * The suffix of the file to which the HTML output is currently being
2075      * written.
2076      */
2077     static String reportFileExt = ".html";
2078 
2079     /**
2080      * The file to which the HTML output is currently being written.
2081      */
2082     static PrintWriter reportFile = null;
2083 
2084     /**
2085      * The object which represents the top of the tree of differences
2086      * between two APIs. It is only used indirectly when emitting a
2087      * navigation bar.
2088      */
2089     static APIDiff apiDiff = null;
2090 
2091     /**
2092      * If set, then do not suggest comments for removals from the first
2093      * sentence of the doc block of the old API.
2094      */
2095     public static boolean noCommentsOnRemovals = false;
2096 
2097     /**
2098      * If set, then do not suggest comments for additions from the first
2099      * sentence of the doc block of the new API.
2100      */
2101     public static boolean noCommentsOnAdditions = false;
2102 
2103     /**
2104      * If set, then do not suggest comments for changes from the first
2105      * sentence of the doc block of the new API.
2106      */
2107     public static boolean noCommentsOnChanges = false;
2108 
2109     /**
2110      * If set, then report changes in documentation (Javadoc comments)
2111      * between the old and the new API. The default is that this is not set.
2112      */
2113     public static boolean reportDocChanges = false;
2114 
2115     /**
2116      * Define the prefix for HTML links to the existing set of Javadoc-
2117      * generated documentation for the new API. E.g. For J2SE1.3.x, use
2118      * "http://java.sun.com/j2se/1.3/docs/api/"
2119      */
2120     public static String newDocPrefix = "../";
2121 
2122     /**
2123      * Define the prefix for HTML links to the existing set of Javadoc-
2124      * generated documentation for the old API.
2125      */
2126     public static String oldDocPrefix = null;
2127 
2128     /** To generate statistical output, set this to true. */
2129     public static boolean doStats = false;
2130 
2131     /**
2132      * The destination directory for output files.
2133      */
2134     public static String outputDir = null;
2135 
2136     /**
2137      * The destination directory for comments files (if not specified, uses outputDir)
2138      */
2139     public static String commentsDir = null;
2140 
2141     /**
2142      * The title used on the first page of the report. By default, this is
2143      * &quot;API Differences Between &lt;name of old API&gt; and
2144      * &lt;name of new API&gt;&quot;. It can be
2145      * set by using the -doctitle option.
2146      */
2147     public static String docTitle = null;
2148 
2149     /**
2150      * The browser window title for the report. By default, this is
2151      * &quot;API Differences Between &lt;name of old API&gt; and
2152      * &lt;name of new API&gt;&quot;. It can be
2153      * set by using the -windowtitle option.
2154      */
2155     public static String windowTitle = null;
2156 
2157     /** The desired background color for JDiff tables. */
2158     static final String bgcolor = "#FFFFFF";
2159 
2160     /** Set to enable debugging output. */
2161     private static final boolean trace = false;
2162 
2163 }
2164