1package jdiff;
2
3import java.io.*;
4import 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@pobox.com
11 */
12class 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