Home | History | Annotate | Download | only in jdiff
      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 (at) pobox.com
     12  */
     13 public class HTMLReportGenerator {
     14 
     15     /** Default constructor. */
     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      */
     35     public Comments getNewComments() {
     36         Collections.sort(newComments_.commentsList_);
     37         return newComments_;
     38     }
     39 
     40     /** Generate the report. */
     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      */
    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      */
    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      */
    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      */
    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.getSignature();
    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.getSignature();
    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      */
    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      */
    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      */
    696     public void writeStartHTMLHeaderWithDate() {
    697         writeStartHTMLHeader(true);
    698     }
    699 
    700     /** Write the start of the HTML header. */
    701     public void writeStartHTMLHeader() {
    702         writeStartHTMLHeader(false);
    703     }
    704 
    705     /** Write the start of the HTML header. */
    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 */
    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      */
    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 
    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. */
    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      */
    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      */
    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 
    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. */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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. */
   2044     public void writeTableEnd() {
   2045         reportFile.println("</TABLE>");
   2046         reportFile.println("&nbsp;");
   2047     }
   2048 
   2049     /** Write a newline out. */
   2050     public void writeText() {
   2051         reportFile.println();
   2052     }
   2053 
   2054     /** Write some text out. */
   2055     public void writeText(String text) {
   2056         reportFile.println(text);
   2057     }
   2058 
   2059     /** Emit some non-breaking space for indentation. */
   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