Home | History | Annotate | Download | only in jdiff
      1 package jdiff;
      2 
      3 import java.io.*;
      4 import java.util.*;
      5 
      6 /**
      7  * Class to generate colored differences between two sections of HTML text.
      8  *
      9  * See the file LICENSE.txt for copyright details.
     10  * @author Matthew Doar, mdoar (at) pobox.com
     11  */
     12 class Diff {
     13 
     14     /**
     15      * Save the differences between the two strings in a DiffOutput object
     16      * for later use.
     17      *
     18      * @param id A per-package unique identifier for each documentation
     19      *           change.
     20      */
     21     static String saveDocDiffs(String pkgName, String className,
     22                                String oldDoc, String newDoc,
     23                                String id, String title) {
     24         // Generate the string which will link to this set of diffs
     25         if (noDocDiffs)
     26             return "Documentation changed from ";
     27         if (oldDoc == null || newDoc == null) {
     28             return "Documentation changed from ";
     29         }
     30 
     31         // Generate the differences.
     32         generateDiffs(pkgName, className, oldDoc, newDoc, id, title);
     33 
     34         return "Documentation <a href=\"" + diffFileName + pkgName +
     35             HTMLReportGenerator.reportFileExt + "#" + id +
     36             "\">changed</a> from ";
     37     }
     38 
     39     /**
     40      * Generate the differences.
     41      */
     42     static void generateDiffs(String pkgName, String className,
     43                               String oldDoc, String newDoc,
     44                               String id, String title) {
     45         String[] oldDocWords = parseDoc(oldDoc);
     46         String[] newDocWords = parseDoc(newDoc);
     47 
     48         DiffMyers diff = new DiffMyers(oldDocWords, newDocWords);
     49         DiffMyers.change script = diff.diff_2(false);
     50         script = mergeDiffs(oldDocWords, newDocWords, script);
     51         String text = "<A NAME=\"" + id + "\"></A>" + title + "<br><br>";
     52         // Generate the differences in blockquotes to cope with unterminated
     53         // HTML tags
     54         text += "<blockquote>";
     55         text = addDiffs(oldDocWords, newDocWords, script, text);
     56         text += "</blockquote>";
     57         docDiffs.add(new DiffOutput(pkgName, className, id, title, text));
     58     }
     59 
     60     /**
     61      * Convert the string to an array of strings, but don't break HTML tags up.
     62      */
     63     static String[] parseDoc(String doc) {
     64         String delimiters = " .,;:?!(){}[]\"'~@#$%^&*+=_-|\\<>/";
     65         StringTokenizer st = new StringTokenizer(doc, delimiters, true);
     66         List docList = new ArrayList();
     67         boolean inTag = false;
     68         String tag = null;
     69         while (st.hasMoreTokens()) {
     70             String tok = st.nextToken();
     71             if (!inTag) {
     72                 if (tok.compareTo("<") == 0) {
     73                     tag = tok;
     74                     if (st.hasMoreTokens()) {
     75                         // See if this really is a tag
     76                         tok = st.nextToken();
     77                         char ch = tok.charAt(0);
     78                         if (Character.isLetter(ch) || ch == '/') {
     79                             inTag = true;
     80                             tag += tok;
     81                         }
     82                     }
     83                     if (!inTag)
     84                       docList.add(tag);
     85                 } else {
     86                     docList.add(tok);
     87                 }
     88             } else {
     89                 // Add all tokens to the tag until the closing > is seen
     90                 if (tok.compareTo(">") == 0) {
     91                     inTag = false;
     92                     tag += tok;
     93                     docList.add(tag);
     94                 } else {
     95                     tag += tok;
     96                 }
     97             }
     98         }
     99         if (inTag) {
    100             // An unterminated tag, or more likely, < used instead of &lt;
    101             // There are no nested tags such as <a <b>> in HTML
    102             docList.add(tag);
    103         }
    104         String[] docWords = new String[docList.size()];
    105         docWords = (String[])docList.toArray(docWords);
    106         return docWords;
    107     }
    108 
    109     /**
    110      * For improved readability, merge changes of the form
    111      *  "delete 1, insert 1, space, delete 1, insert 1"
    112      * to
    113      *  "delete 3, insert 3" (including the space).
    114      *
    115      * @param oldDocWords The original documentation as a String array
    116      * @param newDocWords The new documentation as a String array
    117      */
    118     static DiffMyers.change mergeDiffs(String[] oldDocWords, String[] newDocWords,
    119                                        DiffMyers.change script) {
    120         if (script.link == null)
    121             return script; // Only one change
    122         DiffMyers.change hunk = script;
    123         DiffMyers.change lasthunk = null; // Set to the last potential hunk
    124         int startOld = 0;
    125         for (; hunk != null; hunk = hunk.link) {
    126             int deletes = hunk.deleted;
    127             int inserts = hunk.inserted;
    128             if (lasthunk == null) {
    129                 if (deletes == 1 && inserts == 1) {
    130                     // This is the start of a potential merge
    131                     lasthunk = hunk;
    132                 }
    133                 continue;
    134             } else {
    135                 int first0 = hunk.line0; // Index of first deleted word
    136                 int first1 = hunk.line1; // Index of first inserted word
    137                 if (deletes == 1 && inserts == 1 &&
    138                     oldDocWords[first0 - 1].compareTo(" ") == 0 &&
    139                     newDocWords[first1 - 1].compareTo(" ") == 0 &&
    140                     first0 == lasthunk.line0 + lasthunk.deleted + 1 &&
    141                     first1 == lasthunk.line1 + lasthunk.inserted + 1) {
    142                     // Merge this change into the last change
    143                     lasthunk.deleted += 2;
    144                     lasthunk.inserted += 2;
    145                     lasthunk.link = hunk.link;
    146                 } else {
    147                     lasthunk = null;
    148                 }
    149             }
    150         }
    151         return script;
    152     }
    153 
    154     /**
    155      * Add the differences to the text passed in. The old documentation is
    156      * edited using the edit script provided by the DiffMyers object.
    157      * Do not display diffs in HTML tags.
    158      *
    159      * @param oldDocWords The original documentation as a String array
    160      * @param newDocWords The new documentation as a String array
    161      * @return The text for this documentation difference
    162      */
    163     static String addDiffs(String[] oldDocWords, String[] newDocWords,
    164                            DiffMyers.change script, String text) {
    165         String res = text;
    166         DiffMyers.change hunk = script;
    167         int startOld = 0;
    168         if (trace) {
    169             System.out.println("Old Text:");
    170             for (int i = 0; i < oldDocWords.length; i++) {
    171                 System.out.print(oldDocWords[i]);
    172             }
    173             System.out.println(":END");
    174             System.out.println("New Text:");
    175             for (int i = 0; i < newDocWords.length; i++) {
    176                 System.out.print(newDocWords[i]);
    177             }
    178             System.out.println(":END");
    179         }
    180 
    181         for (; hunk != null; hunk = hunk.link) {
    182             int deletes = hunk.deleted;
    183             int inserts = hunk.inserted;
    184             if (deletes == 0 && inserts == 0) {
    185                 continue; // Not clear how this would occur, but handle it
    186             }
    187 
    188             // Determine the range of word and delimiter numbers involved
    189             // in each file.
    190             int first0 = hunk.line0; // Index of first deleted word
    191             // Index of last deleted word, invalid if deletes == 0
    192             int last0 = hunk.line0 + hunk.deleted - 1;
    193             int first1 = hunk.line1; // Index of first inserted word
    194             // Index of last inserted word, invalid if inserts == 0
    195             int last1 = hunk.line1 + hunk.inserted - 1;
    196 
    197             if (trace) {
    198                 System.out.println("HUNK: ");
    199                 System.out.println("inserts: " + inserts);
    200                 System.out.println("deletes: " + deletes);
    201                 System.out.println("first0: " + first0);
    202                 System.out.println("last0: " + last0);
    203                 System.out.println("first1: " + first1);
    204                 System.out.println("last1: " + last1);
    205             }
    206 
    207             // Emit the original document up to this change
    208             for (int i = startOld; i < first0; i++) {
    209                 res += oldDocWords[i];
    210             }
    211             // Record where to start the next hunk from
    212             startOld = last0 + 1;
    213             // Emit the deleted words, but struck through
    214             // but do not emit deleted HTML tags
    215             if (deletes != 0) {
    216                 boolean inStrike = false;
    217                 for (int i = first0; i <= last0; i++) {
    218                     if (!oldDocWords[i].startsWith("<") &&
    219                         !oldDocWords[i].endsWith(">")) {
    220                         if (!inStrike) {
    221                             if (deleteEffect == 0)
    222                                 res += "<strike>";
    223                             else if (deleteEffect == 1)
    224                                 res += "<span style=\"background: #FFCCCC\">";
    225                             inStrike = true;
    226                         }
    227                         res += oldDocWords[i];
    228                     }
    229                 }
    230                 if (inStrike) {
    231                     if (deleteEffect == 0)
    232                         res += "</strike>";
    233                     else if (deleteEffect == 1)
    234                         res += "</span>";
    235                 }
    236             }
    237             // Emit the inserted words, but do not emphasise new HTML tags
    238             if (inserts != 0) {
    239                 boolean inEmph = false;
    240                 for (int i = first1; i <= last1; i++) {
    241                     if (!newDocWords[i].startsWith("<") &&
    242                         !newDocWords[i].endsWith(">")) {
    243                         if (!inEmph) {
    244                             if (insertEffect == 0)
    245                                 res += "<font color=\"red\">";
    246                             else if (insertEffect == 1)
    247                                 res += "<span style=\"background: #FFFF00\">";
    248                             inEmph = true;
    249                         }
    250                     }
    251                     res += newDocWords[i];
    252                 }
    253                 if (inEmph) {
    254                     if (insertEffect == 0)
    255                         res += "</font>";
    256                     else if (insertEffect == 1)
    257                         res += "</span>";
    258                 }
    259             }
    260         } //for (; hunk != null; hunk = hunk.link)
    261         // Print out the remaining part of the old text
    262         for (int i = startOld; i < oldDocWords.length; i++) {
    263             res += oldDocWords[i];
    264         }
    265         return res;
    266     }
    267 
    268     /**
    269      * Emit all the documentation differences into one file per package.
    270      */
    271     static void emitDocDiffs(String fullReportFileName) {
    272         Collections.sort(docDiffs);
    273 
    274         DiffOutput[] docDiffsArr = new DiffOutput[docDiffs.size()];
    275         docDiffsArr = (DiffOutput[])docDiffs.toArray(docDiffsArr);
    276 
    277         for (int i = 0; i < docDiffsArr.length; i++) {
    278             DiffOutput diffOutput = docDiffsArr[i];
    279             if (currPkgName == null ||
    280                 currPkgName.compareTo(diffOutput.pkgName_) != 0) {
    281                 // Open a different file for each package, add the HTML header,
    282                 // the navigation bar and some preamble.
    283                 if (currPkgName != null)
    284                     closeDiffFile(); // Close the existing file
    285                 // Create the HTML link to the previous package
    286                 String prevPkgName = currPkgName;
    287                 if (currPkgName != null) {
    288                     prevPkgName = diffFileName + docDiffsArr[i-1].pkgName_ +
    289                     HTMLReportGenerator.reportFileExt;
    290                 }
    291                 // Set the current package name
    292                 currPkgName = diffOutput.pkgName_;
    293                 // Create the HTML link to the next package
    294                 String nextPkgName = null;
    295                 for (int j = i; j < docDiffsArr.length; j++) {
    296                     if (currPkgName.compareTo(docDiffsArr[j].pkgName_) != 0) {
    297                         nextPkgName = diffFileName + docDiffsArr[j].pkgName_ +
    298                             HTMLReportGenerator.reportFileExt;
    299                         break;
    300                     }
    301                 }
    302 
    303                 String fullDiffFileName = fullReportFileName +
    304                     JDiff.DIR_SEP + diffFileName + currPkgName +
    305                     HTMLReportGenerator.reportFileExt;
    306                 // Create the output file
    307                 try {
    308                     FileOutputStream fos = new FileOutputStream(fullDiffFileName);
    309                     diffFile = new PrintWriter(fos);
    310 
    311                     // Write the HTML header
    312                     diffFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\"\"" + RootDocToXML.baseURI + "/TR/REC-html40/frameset.dtd\">");
    313                     diffFile.println("<HTML>");
    314                     diffFile.println("<HEAD>");
    315                     diffFile.println("<meta name=\"generator\" content=\"JDiff v" + JDiff.version + "\">");
    316                     diffFile.println("<!-- Generated by the JDiff Javadoc doclet -->");
    317                     diffFile.println("<!-- (" + JDiff.jDiffLocation + ") -->");
    318 //                    diffFile.println("<!-- on " + new Date() + " -->");
    319                     diffFile.println("<meta name=\"description\" content=\"" + JDiff.jDiffDescription + "\">");
    320                     diffFile.println("<meta name=\"keywords\" content=\"" + JDiff.jDiffKeywords + "\">");
    321                     diffFile.println("<LINK REL=\"stylesheet\" TYPE=\"text/css\" HREF=\"" + "../" + "stylesheet-jdiff.css\" TITLE=\"Style\">");
    322                     diffFile.println("<TITLE>");
    323                     diffFile.println(currPkgName + " Documentation Differences");
    324                     diffFile.println("</TITLE>");
    325                     diffFile.println("</HEAD>");
    326                     diffFile.println("<BODY>");
    327 
    328                     // Write the navigation bar
    329                     diffFile.println("<!-- Start of nav bar -->");
    330                     diffFile.println("<TABLE summary=\"Navigation bar\" BORDER=\"0\" WIDTH=\"100%\" CELLPADDING=\"1\" CELLSPACING=\"0\">");
    331                     diffFile.println("<TR>");
    332                     diffFile.println("<TD COLSPAN=2 BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\">");
    333                     diffFile.println("  <TABLE summary=\"Navigation bar\" BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"3\">");
    334                     diffFile.println("    <TR ALIGN=\"center\" VALIGN=\"top\">");
    335                     // Always have a link to the Javadoc files
    336                     String pkgRef = currPkgName;
    337                     pkgRef = pkgRef.replace('.', '/');
    338                     pkgRef = HTMLReportGenerator.newDocPrefix + pkgRef + "/package-summary";
    339                     diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + pkgRef + ".html\" target=\"_top\"><FONT CLASS=\"NavBarFont1\"><B><tt>" + APIDiff.newAPIName_ + "</tt></B></FONT></A>&nbsp;</TD>");
    340                     diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + HTMLReportGenerator.reportFileName + "-summary" + HTMLReportGenerator.reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Overview</B></FONT></A>&nbsp;</TD>");
    341                     diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> &nbsp;<FONT CLASS=\"NavBarFont1\">Package</FONT>&nbsp;</TD>");
    342                     diffFile.println("      <TD BGCOLOR=\"#FFFFFF\" CLASS=\"NavBarCell1\"> &nbsp;<FONT CLASS=\"NavBarFont1\">Class</FONT>&nbsp;</TD>");
    343                     if (!Diff.noDocDiffs) {
    344                         diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + Diff.diffFileName + "index" + HTMLReportGenerator.reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Text Changes</B></FONT></A>&nbsp;</TD>");
    345                     }
    346                     if (HTMLReportGenerator.doStats) {
    347                         diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"jdiff_statistics" + HTMLReportGenerator.reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Statistics</B></FONT></A>&nbsp;</TD>");
    348                     }
    349                     diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"jdiff_help" + HTMLReportGenerator.reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Help</B></FONT></A>&nbsp;</TD>");
    350                     diffFile.println("    </TR>");
    351                     diffFile.println("  </TABLE>");
    352                     diffFile.println("</TD>");
    353 
    354                     // The right hand side title
    355                     diffFile.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>");
    356                     diffFile.println("</TR>");
    357 
    358                     // Links for previous and next, and frames and no frames
    359                     diffFile.println("<TR>");
    360                     diffFile.println("  <TD BGCOLOR=\"" + HTMLReportGenerator.bgcolor + "\" CLASS=\"NavBarCell2\"><FONT SIZE=\"-2\">");
    361                     if (prevPkgName != null)
    362                         diffFile.println("  <A HREF=\"" + prevPkgName + "\"><B>PREV PACKAGE</B></A>  &nbsp;");
    363                     else
    364                         diffFile.println("  <B>PREV PACKAGE</B>  &nbsp;");
    365                     if (nextPkgName != null)
    366                         diffFile.println("  &nbsp;<A HREF=\"" + nextPkgName + "\"><B>NEXT PACKAGE</B></A>");
    367                     else
    368                         diffFile.println("  &nbsp;<B>NEXT PACKAGE</B>");
    369                     diffFile.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
    370                     diffFile.println("  <A HREF=\"" + "../" + HTMLReportGenerator.reportFileName + HTMLReportGenerator.reportFileExt + "\" TARGET=\"_top\"><B>FRAMES</B></A>  &nbsp;");
    371                     diffFile.println("  &nbsp;<A HREF=\"" + diffFileName + currPkgName + HTMLReportGenerator.reportFileExt + "\" TARGET=\"_top\"><B>NO FRAMES</B></A></FONT></TD>");
    372                     diffFile.println("  <TD BGCOLOR=\"" + HTMLReportGenerator.bgcolor + "\" CLASS=\"NavBarCell2\">&nbsp;</TD>");
    373                     diffFile.println("</TR>");
    374 
    375                     diffFile.println("</TABLE>");
    376                     diffFile.println("<HR>");
    377                     diffFile.println("<!-- End of nav bar -->");
    378 
    379                     diffFile.println("<h2>");
    380                     diffFile.println(currPkgName + " Documentation Differences");
    381                     diffFile.println("</h2>");
    382                     diffFile.println();
    383                     diffFile.println("<blockquote>");
    384                     diffFile.println("This file contains all the changes in documentation in the package <code>" + currPkgName + "</code> as colored differences.");
    385                     if (deleteEffect == 0)
    386                         diffFile.println("Deletions are shown <strike>like this</strike>, and");
    387                     else if (deleteEffect == 1)
    388                         diffFile.println("Deletions are shown <span style=\"background: #FFCCCC\">like this</span>, and");
    389                     if (insertEffect == 0)
    390                         diffFile.println("additions are shown in red <font color=\"red\">like this</font>.");
    391                     else if (insertEffect == 1)
    392                         diffFile.println("additions are shown <span style=\"background: #FFFF00\">like this</span>.");
    393                     diffFile.println("</blockquote>");
    394 
    395                     diffFile.println("<blockquote>");
    396                     diffFile.println("If no deletions or additions are shown in an entry, the HTML tags will be what has changed. The <i>new</i> HTML tags are shown in the differences. ");
    397                     diffFile.println("If no documentation existed, and then some was added in a later version, this change is noted in the appropriate class pages of differences, but the change is not shown on this page. Only changes in existing text are shown here. ");
    398                     diffFile.println("Similarly, documentation which was inherited from another class or interface is not shown here.");
    399                     diffFile.println("</blockquote>");
    400 
    401                     diffFile.println("<blockquote>");
    402                     diffFile.println(" Note that an HTML error in the new documentation may cause the display of other documentation changes to be presented incorrectly. For instance, failure to close a &lt;code&gt; tag will cause all subsequent paragraphs to be displayed differently.");
    403                     diffFile.println("</blockquote>");
    404                     diffFile.println("<hr>");
    405                     diffFile.println();
    406 
    407                 } catch(IOException e) {
    408                     System.out.println("IO Error while attempting to create " + fullDiffFileName);
    409                     System.out.println("Error: " + e.getMessage());
    410                     System.exit(1);
    411                 }
    412             } // if (currPkgName == null || currPkgName.compareTo(diffOutput.pkgName_) != 0)
    413             // Now add the documentation difference text
    414             diffFile.println(diffOutput.text_);
    415             // Separate with a horizontal line
    416             if (i != docDiffsArr.length - 1 &&
    417                 diffOutput.className_ != null &&
    418                 docDiffsArr[i+1].className_ != null &&
    419                 diffOutput.className_.compareTo(docDiffsArr[i+1].className_) != 0)
    420                 diffFile.println("<hr align=\"left\" width=\"100%\">");
    421 //            else
    422 //                diffFile.println("<hr align=\"left\" width=\"50%\">");
    423         } // for (i = 0;
    424         if (currPkgName != null)
    425             closeDiffFile(); // Close the existing file
    426 
    427         // Emit the single file which is the index to all documentation changes
    428         emitDocDiffIndex(fullReportFileName, docDiffsArr);
    429     }
    430 
    431     /**
    432      * Emit the single file which is the index to all documentation changes.
    433      */
    434     public static void emitDocDiffIndex(String fullReportFileName,
    435                                         DiffOutput[] docDiffsArr) {
    436 
    437         String fullDiffFileName = fullReportFileName +
    438             JDiff.DIR_SEP + diffFileName + "index" +
    439             HTMLReportGenerator.reportFileExt;
    440 
    441         // Create the output file
    442         try {
    443             FileOutputStream fos = new FileOutputStream(fullDiffFileName);
    444             diffFile = new PrintWriter(fos);
    445 
    446             // Write the HTML header
    447             diffFile.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\"\"" + RootDocToXML.baseURI + "/TR/REC-html40/frameset.dtd\">");
    448             diffFile.println("<HTML>");
    449             diffFile.println("<HEAD>");
    450             diffFile.println("<meta name=\"generator\" content=\"JDiff v" + JDiff.version + "\">");
    451             diffFile.println("<!-- Generated by the JDiff Javadoc doclet -->");
    452             diffFile.println("<!-- (" + JDiff.jDiffLocation + ") -->");
    453 //            diffFile.println("<!-- on " + new Date() + " -->");
    454             diffFile.println("<meta name=\"description\" content=\"" + JDiff.jDiffDescription + "\">");
    455             diffFile.println("<meta name=\"keywords\" content=\"" + JDiff.jDiffKeywords + "\">");
    456             diffFile.println("<LINK REL=\"stylesheet\" TYPE=\"text/css\" HREF=\"" + "../" + "stylesheet-jdiff.css\" TITLE=\"Style\">");
    457             diffFile.println("<TITLE>");
    458             diffFile.println("All Documentation Differences");
    459             diffFile.println("</TITLE>");
    460             diffFile.println("</HEAD>");
    461             diffFile.println("<BODY>");
    462 
    463             // Write the navigation bar
    464             diffFile.println("<!-- Start of nav bar -->");
    465             diffFile.println("<TABLE summary=\"Navigation bar\" BORDER=\"0\" WIDTH=\"100%\" CELLPADDING=\"1\" CELLSPACING=\"0\">");
    466             diffFile.println("<TR>");
    467             diffFile.println("<TD COLSPAN=2 BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\">");
    468             diffFile.println("  <TABLE summary=\"Navigation bar\" BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"3\">");
    469             diffFile.println("    <TR ALIGN=\"center\" VALIGN=\"top\">");
    470             // Always have a link to the Javadoc files
    471             diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + HTMLReportGenerator.newDocPrefix + "index.html\" target=\"_top\"><FONT CLASS=\"NavBarFont1\"><B><tt>" + APIDiff.newAPIName_ + "</tt></B></FONT></A>&nbsp;</TD>");
    472             diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"" + HTMLReportGenerator.reportFileName + "-summary" + HTMLReportGenerator.reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Overview</B></FONT></A>&nbsp;</TD>");
    473             diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> &nbsp;<FONT CLASS=\"NavBarFont1\">Package</FONT>&nbsp;</TD>");
    474             diffFile.println("      <TD BGCOLOR=\"#FFFFFF\" CLASS=\"NavBarCell1\"> &nbsp;<FONT CLASS=\"NavBarFont1\">Class</FONT>&nbsp;</TD>");
    475             if (!Diff.noDocDiffs) {
    476                 diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1Rev\"> <FONT CLASS=\"NavBarFont1Rev\"><B>Text Changes</B></FONT>&nbsp;</TD>");
    477             }
    478             if (HTMLReportGenerator.doStats) {
    479                 diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"jdiff_statistics" + HTMLReportGenerator.reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Statistics</B></FONT></A>&nbsp;</TD>");
    480             }
    481             diffFile.println("      <TD BGCOLOR=\"#EEEEFF\" CLASS=\"NavBarCell1\"> <A HREF=\"jdiff_help" + HTMLReportGenerator.reportFileExt + "\"><FONT CLASS=\"NavBarFont1\"><B>Help</B></FONT></A>&nbsp;</TD>");
    482             diffFile.println("    </TR>");
    483             diffFile.println("  </TABLE>");
    484             diffFile.println("</TD>");
    485 
    486             // The right hand side title
    487             diffFile.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>");
    488             diffFile.println("</TR>");
    489 
    490             // Links for frames and no frames
    491             diffFile.println("<TR>");
    492             diffFile.println("  <TD BGCOLOR=\"" + HTMLReportGenerator.bgcolor + "\" CLASS=\"NavBarCell2\"><FONT SIZE=\"-2\">");
    493             diffFile.println("  <A HREF=\"" + "../" + HTMLReportGenerator.reportFileName + HTMLReportGenerator.reportFileExt + "\" TARGET=\"_top\"><B>FRAMES</B></A>  &nbsp;");
    494             diffFile.println("  &nbsp;<A HREF=\"" + diffFileName + "index" + HTMLReportGenerator.reportFileExt + "\" TARGET=\"_top\"><B>NO FRAMES</B></A></FONT></TD>");
    495             diffFile.println("  <TD BGCOLOR=\"" + HTMLReportGenerator.bgcolor + "\" CLASS=\"NavBarCell2\">&nbsp;</TD>");
    496             diffFile.println("</TR>");
    497 
    498             diffFile.println("</TABLE>");
    499             diffFile.println("<HR>");
    500             diffFile.println("<!-- End of nav bar -->");
    501 
    502             diffFile.println("<h2>");
    503             diffFile.println("All Documentation Differences");
    504             diffFile.println("</h2>");
    505             diffFile.println();
    506 
    507             // For each package and class, add the first DiffOutput to
    508             // the hash table. Used when generating navigation bars.
    509             boolean firstPackage = true; // Set for the first package
    510             boolean firstClass = true; // Set for first class in a package
    511             boolean firstCtor = true; // Set for first ctor in a class
    512             boolean firstMethod = true; // Set for first method in a class
    513             boolean firstField = true; // Set for first field in a class
    514             for (int i = 0; i < docDiffsArr.length; i++) {
    515                 DiffOutput diffOutput = docDiffsArr[i];
    516                 String link = "<a href=\"" + Diff.diffFileName + diffOutput.pkgName_ + HTMLReportGenerator.reportFileExt + "#" + diffOutput.id_ + "\">";
    517 
    518                 // See if the package name changed
    519                 if (firstPackage || diffOutput.pkgName_.compareTo(docDiffsArr[i-1].pkgName_) != 0) {
    520                     if (firstPackage) {
    521                         firstPackage = false;
    522                     } else {
    523                         diffFile.println("<br>");
    524                     }
    525                     firstClass = true;
    526                     firstCtor = true;
    527                     firstMethod = true;
    528                     firstField = true;
    529                     String id = diffOutput.pkgName_ + "!package";
    530                     firstDiffOutput.put(id, id);
    531                     if (diffOutput.className_ == null) {
    532                         diffFile.println("<A NAME=\"" + id + "\"></A>" + link + "Package <b>" + diffOutput.pkgName_ + "</b></a><br>");
    533                     } else {
    534                         diffFile.println("<A NAME=\"" + id + "\"></A>" + "Package <b>" + diffOutput.pkgName_ + "</b><br>");
    535                     }
    536                 }
    537                 // See if the class name changed
    538                 if (diffOutput.className_ != null &&
    539                     (firstClass ||
    540                      diffOutput.className_.compareTo(docDiffsArr[i-1].className_) != 0)) {
    541                     if (firstClass) {
    542                         firstClass = false;
    543                     } else {
    544                         diffFile.println("<br>");
    545                     }
    546                     firstCtor = true;
    547                     firstMethod = true;
    548                     firstField = true;
    549                     String id = diffOutput.pkgName_ + "." + diffOutput.className_ + "!class";
    550                     firstDiffOutput.put(id, id);
    551                     if (diffOutput.id_.endsWith("!class")) {
    552                         diffFile.println("<A NAME=\"" + id + "\"></A>&nbsp;&nbsp;Class " + link + diffOutput.className_ + "</a><br>");
    553                     } else {
    554                         diffFile.println("<A NAME=\"" + id + "\"></A>&nbsp;&nbsp;Class " + diffOutput.className_ + "<br>");
    555                     }
    556                 }
    557                 // Work out what kind of member this is, and
    558                 // display it appropriately
    559                 if (diffOutput.className_ != null &&
    560                     !diffOutput.id_.endsWith("!class")) {
    561                     int ctorIdx = diffOutput.id_.indexOf(".ctor");
    562                     if (ctorIdx != -1) {
    563                         diffFile.println("&nbsp;&nbsp;&nbsp;&nbsp;" + link + diffOutput.className_ + diffOutput.id_.substring(ctorIdx + 5) + "</a><br>");
    564                     } else {
    565                         int methodIdx = diffOutput.id_.indexOf(".dmethod.");
    566                         if (methodIdx != -1) {
    567                             diffFile.println("&nbsp;&nbsp;&nbsp;&nbsp;"  + "Method " + link + diffOutput.id_.substring(methodIdx + 9) + "</a><br>");
    568                         } else {
    569                             int fieldIdx = diffOutput.id_.indexOf(".field.");
    570                             if (fieldIdx != -1) {
    571                                 diffFile.println("&nbsp;&nbsp;&nbsp;&nbsp;" + "Field " + link + diffOutput.id_.substring(fieldIdx + 7) + "</a><br>");
    572                             }
    573                         } //if (methodIdx != -1)
    574                     } //if (ctorIdx != -1)
    575                 } //diffOutput.className_ != null
    576             }
    577         } catch(IOException e) {
    578             System.out.println("IO Error while attempting to create " + fullDiffFileName);
    579             System.out.println("Error: " + e.getMessage());
    580             System.exit(1);
    581         }
    582         closeDiffFile();
    583     }
    584 
    585     /**
    586      * Emit the HTML footer and close the diff file.
    587      */
    588     public static void closeDiffFile() {
    589         if (diffFile != null) {
    590             // Write the HTML footer
    591             diffFile.println();
    592             diffFile.println("</BODY>");
    593             diffFile.println("</HTML>");
    594             diffFile.close();
    595         }
    596     }
    597 
    598     /**
    599      * Current file where documentation differences are written as colored
    600      * differences.
    601      */
    602     public static PrintWriter diffFile = null;
    603 
    604     /**
    605      * Base name of the current file where documentation differences are
    606      * written as colored differences.
    607      */
    608     public static String diffFileName = "docdiffs_";
    609 
    610     /**
    611      * The name of the current package, used to create diffFileName.
    612      */
    613     private static String currPkgName = null;
    614 
    615     /**
    616      * If set, then do not generate colored diffs for documentation.
    617      * Default is true.
    618      */
    619     public static boolean noDocDiffs = true;
    620 
    621     /**
    622      * Define the type of emphasis for deleted words.
    623      * 0 strikes the words through.
    624      * 1 outlines the words in light grey.
    625      */
    626     public static int deleteEffect = 0;
    627 
    628     /**
    629      * Define the type of emphasis for inserted words.
    630      * 0 colors the words red.
    631      * 1 outlines the words in yellow, like a highlighter.
    632      */
    633     public static int insertEffect = 1;
    634 
    635     /**
    636      * For each package and class, the first DiffOutput is added to
    637      * this hash table. Used when generating navigation bars.
    638      */
    639     public static Hashtable firstDiffOutput = new Hashtable();
    640 
    641     /**
    642      * If set, then show changes in implementation-related modifiers such as
    643      * native and synchronized. For more information, see
    644      * http://java.sun.com/j2se/1.4.1/docs/tooldocs/solaris/javadoc.html#generatedapideclarations
    645      */
    646     public static boolean showAllChanges = false;
    647 
    648     /** The list of documentation differences. */
    649     private static List docDiffs = new ArrayList(); // DiffOutput[]
    650 
    651     /** Set to enable increased logging verbosity for debugging. */
    652     private static boolean trace = false;
    653 
    654 }
    655