1package jdiff;
2
3import java.util.*;
4import java.io.*;
5
6/**
7 * Emit HTML indexes which appear in the bottom left frame in the report.
8 * All indexes are links to JDiff-generated pages.
9 *
10 * See the file LICENSE.txt for copyright details.
11 * @author Matthew Doar, mdoar@pobox.com
12 */
13public class HTMLIndexes {
14
15    /** Constructor. */
16    public HTMLIndexes(HTMLReportGenerator h) {
17        h_ = h;
18    }
19
20    /** The HTMLReportGenerator instance used to write HTML. */
21    private HTMLReportGenerator h_ = null;
22
23    /** Emit all the bottom left frame index files. */
24    public void emitAllBottomLeftFiles(String packagesIndexName,
25                                       String classesIndexName,
26                                       String constructorsIndexName,
27                                       String methodsIndexName,
28                                       String fieldsIndexName,
29                                       String allDiffsIndexName,
30                                       APIDiff apiDiff) {
31
32        // indexType values: 0 = removals only, 1 = additions only,
33        // 2 = changes only. 3 = all differences. Run all differences
34        // first for all program element types so we know whether there
35        // are any removals etc for the allDiffs index.
36        emitBottomLeftFile(packagesIndexName, apiDiff, 3, "Package");
37        emitBottomLeftFile(classesIndexName, apiDiff, 3, "Class");
38        emitBottomLeftFile(constructorsIndexName, apiDiff, 3, "Constructor");
39        emitBottomLeftFile(methodsIndexName, apiDiff, 3, "Method");
40        emitBottomLeftFile(fieldsIndexName, apiDiff, 3, "Field");
41        // The allindex must be done last, since it uses the results from
42        // the previous ones
43        emitBottomLeftFile(allDiffsIndexName, apiDiff, 3, "All");
44        // Now generate the other indexes
45        for (int indexType = 0; indexType < 3; indexType++) {
46            emitBottomLeftFile(packagesIndexName, apiDiff, indexType, "Package");
47            emitBottomLeftFile(classesIndexName, apiDiff, indexType, "Class");
48            emitBottomLeftFile(constructorsIndexName, apiDiff, indexType, "Constructor");
49            emitBottomLeftFile(methodsIndexName, apiDiff, indexType, "Method");
50            emitBottomLeftFile(fieldsIndexName, apiDiff, indexType, "Field");
51            emitBottomLeftFile(allDiffsIndexName, apiDiff, indexType, "All");
52        }
53        if (missingSincesFile != null)
54            missingSincesFile.close();
55    }
56
57    /**
58     * Emit a single bottom left frame with the given kind of differences for
59     * the given program element type in an alphabetical index.
60     *
61     * @param indexBaseName The base name of the index file.
62     * @param apiDiff The root element containing all the API differences.
63     * @param indexType 0 = removals only, 1 = additions only,
64     *                  2 = changes only, 3 = all differences,
65     * @param programElementType "Package", "Class", "Constructor",
66     *                           "Method", "Field" or "All".
67     */
68    public void emitBottomLeftFile(String indexBaseName,
69                                   APIDiff apiDiff, int indexType,
70                                   String programElementType) {
71        String filename = indexBaseName;
72        try {
73            String title = "Indexes";
74            if (indexType == 0) {
75                filename += "_removals" + h_.reportFileExt;
76                title = programElementType + " Removals Index";
77            } else if (indexType == 1) {
78                filename += "_additions" + h_.reportFileExt;
79                title = programElementType + " Additions Index";
80            } else if (indexType == 2) {
81                filename += "_changes" + h_.reportFileExt;
82                title = programElementType + " Changes Index";
83            } else if (indexType == 3) {
84                filename += "_all" + h_.reportFileExt;
85                title = programElementType + " Differences Index";
86            }
87
88            FileOutputStream fos = new FileOutputStream(filename);
89            h_.reportFile = new PrintWriter(fos);
90            h_.writeStartHTMLHeader();
91            h_.writeHTMLTitle(title);
92            h_.writeStyleSheetRef();
93            h_.writeText("</HEAD>");
94            h_.writeText("<BODY>");
95
96            if (programElementType.compareTo("Package") == 0) {
97                emitPackagesIndex(apiDiff, indexType);
98            } else if (programElementType.compareTo("Class") == 0) {
99                emitClassesIndex(apiDiff, indexType);
100            } else if (programElementType.compareTo("Constructor") == 0) {
101                emitConstructorsIndex(apiDiff, indexType);
102            } else if (programElementType.compareTo("Method") == 0) {
103                emitMethodsIndex(apiDiff, indexType);
104            } else if (programElementType.compareTo("Field") == 0) {
105                emitFieldsIndex(apiDiff, indexType);
106            } else if (programElementType.compareTo("All") == 0) {
107                emitAllDiffsIndex(apiDiff, indexType);
108            } else{
109                System.out.println("Error: unknown program element type.");
110                System.exit(3);
111            }
112
113            h_.writeHTMLFooter();
114            h_.reportFile.close();
115        } catch(IOException e) {
116            System.out.println("IO Error while attempting to create " + filename);
117            System.out.println("Error: " + e.getMessage());
118            System.exit(1);
119        }
120    }
121
122    /**
123     * Generate a small header of letters which link to each section, but
124     * do not emit a linked letter for the current section. Finish the list off
125     * with a link to the top of the index.
126     * Caching the results of this function would save about 10s with large APIs.
127     */
128    private void generateLetterIndex(List list, char currChar, boolean larger) {
129        if (larger)
130            return; // Currently not using the larger functionality
131        int size = -2;
132	if (larger)
133            size = -1;
134        Iterator iter = null;
135        if (isAllNames)
136            iter = allNames.iterator();
137        else
138            iter = list.iterator();
139        char oldsw = '\0';
140        while (iter.hasNext()) {
141            Index entry = (Index)(iter.next());
142            char sw = entry.name_.charAt(0);
143            char swu = Character.toUpperCase(sw);
144            if (swu != Character.toUpperCase(oldsw)) {
145                // Don't emit a reference to the current letter
146                if (Character.toUpperCase(sw) != Character.toUpperCase(currChar)) {
147                    if (swu == '_') {
148                        h_.writeText("<a href=\"#" + swu + "\"><font size=\"" + size + "\">" + "underscore" + "</font></a> ");
149                    } else {
150                        h_.writeText("<a href=\"#" + swu + "\"><font size=\"" + size + "\">" + swu + "</font></a> ");
151                    }
152                }
153                oldsw = sw;
154            }
155        }
156        h_.writeText(" <a href=\"#topheader\"><font size=\"" + size + "\">TOP</font></a>");
157        h_.writeText("<p><div style=\"line-height:1.5em;color:black\">");
158    }
159
160    /**
161     * Emit a header for an index, including suitable links for removed,
162     * added and changes sub-indexes.
163     */
164    private void emitIndexHeader(String indexName, int indexType,
165                                 boolean hasRemovals,
166                                 boolean hasAdditions, boolean hasChanges) {
167        String linkIndexName = indexName.toLowerCase();
168        boolean isAllDiffs = false;
169        if (indexName.compareTo("All Differences") == 0) {
170            linkIndexName = "alldiffs";
171            isAllDiffs = true;
172        }
173        h_.writeText("<a NAME=\"topheader\"></a>"); // Named anchor
174        h_.writeText("<table summary=\"Index for " +  indexName + "\" width=\"100%\" class=\"index\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
175        h_.writeText("  <tr>");
176        h_.writeText("  <th class=\"indexHeader\">");
177        h_.writeText("    Filter the Index:");
178        h_.writeText("  </th>");
179        h_.writeText("  </tr>");
180        h_.writeText("  <tr>");
181        h_.writeText("  <td class=\"indexText\" style=\"line-height:1.5em;padding-left:2em;\">");
182//        h_.writeText("  <div style=\"line-height:1.25em;padding-left:1em;>\">");
183//        h_.writeText("  <FONT SIZE=\"-1\">");
184        // The index name is also a hidden link to the *index_all page
185        if (indexType == 3) {
186             h_.writeText("<b>" + indexName + "</b>"); }
187        else if (isAllDiffs) {
188            h_.writeText("<a href=\"" + linkIndexName + "_index_all" + h_.reportFileExt + "\" class=\"hiddenlink\">" + indexName + "</a>");
189        }
190        else {
191            h_.writeText("<a href=\"" + linkIndexName + "_index_all" + h_.reportFileExt + "\" class=\"staysblack\">All " + indexName + "</a>");
192        }
193//        h_.writeText("  </FONT>");
194
195        h_.writeText("  <br>");
196//        h_.writeText("  <FONT SIZE=\"-1\">");
197        if (hasRemovals) {
198          if (indexType == 0) {
199            h_.writeText("<b>Removals</b>");
200          } else {
201            h_.writeText("<A HREF=\"" + linkIndexName + "_index_removals" + h_.reportFileExt + "\" class=\"hiddenlink\">Removals</A>");
202          }
203        } else {
204            h_.writeText("<font color=\"#999999\">Removals</font>");
205        }
206//        h_.writeText("  </FONT>");
207
208        h_.writeText("  <br>");
209//      h_.writeText("  <FONT SIZE=\"-1\">");
210        if (hasAdditions) {
211          if (indexType == 1) {
212            h_.writeText("<b>Additions</b>");
213          } else {
214            h_.writeText("<A HREF=\"" + linkIndexName + "_index_additions" + h_.reportFileExt + "\"class=\"hiddenlink\">Additions</A>");
215          }
216        } else {
217            h_.writeText("<font color=\"#999999\">Additions</font>");
218        }
219//        h_.writeText("  </FONT>");
220
221        h_.writeText("  <br>");
222//         h_.writeText("  <FONT SIZE=\"-1\">");
223        if (hasChanges) {
224          if (indexType == 2) {
225            h_.writeText("<b>Changes</b>");
226          } else {
227            h_.writeText("<A HREF=\"" + linkIndexName + "_index_changes" + h_.reportFileExt + "\"class=\"hiddenlink\">Changes</A>");
228          }
229        } else {
230            h_.writeText("<font color=\"#999999\">Changes</font>");
231        }
232//        h_.writeText("  </FONT>");
233//        h_.writeText("  </div>");
234        h_.writeText("  </td>");
235        h_.writeText("  </tr>");
236        h_.writeText("</table>");
237        h_.writeText("<font size=\"-2\"><strong>Bold</strong>&nbsp;indicates&nbsp;New;&nbsp;<strike>Strike</strike>&nbsp;indicates&nbsp;deleted</font>");
238        h_.writeText("  </br>");
239
240    }
241
242    /** Emit the index of packages, which appears in the bottom left frame. */
243    public void emitPackagesIndex(APIDiff apiDiff, int indexType) {
244        // Add all the names of packages to a new list, to be sorted later
245        packageNames = new ArrayList(); // Index[]
246        boolean hasRemovals = false;
247        if (apiDiff.packagesRemoved.size() != 0)
248            hasRemovals = true;
249        boolean hasAdditions = false;
250        if (apiDiff.packagesAdded.size() != 0)
251            hasAdditions = true;
252        boolean hasChanges = false;
253        if (apiDiff.packagesChanged.size() != 0)
254            hasChanges = true;
255        recordDiffs(hasRemovals, hasAdditions, hasChanges);
256        Iterator iter = apiDiff.packagesRemoved.iterator();
257        while ((indexType == 3 || indexType == 0) && iter.hasNext()) {
258            PackageAPI pkg = (PackageAPI)(iter.next());
259            packageNames.add(new Index(pkg.name_, 0));
260        }
261        iter = apiDiff.packagesAdded.iterator();
262        while ((indexType == 3 || indexType == 1) && iter.hasNext()) {
263            PackageAPI pkg = (PackageAPI)(iter.next());
264            packageNames.add(new Index(pkg.name_, 1));
265        }
266        iter = apiDiff.packagesChanged.iterator();
267        while ((indexType == 3 || indexType == 2) && iter.hasNext()) {
268            PackageDiff pkg = (PackageDiff)(iter.next());
269            packageNames.add(new Index(pkg.name_, 2));
270        }
271        Collections.sort(packageNames);
272
273        // No letter index needed for packages
274
275        // Now emit all the package names and links to their respective files
276        emitIndexHeader("Packages", indexType, hasRemovals, hasAdditions, hasChanges);
277
278        // Extra line because no index is emitted
279        h_.writeText("<br>");
280
281        // Package names are unique, so no need to check for duplicates.
282        iter = packageNames.iterator();
283        char oldsw = '\0';
284        while (iter.hasNext()) {
285            Index pkg = (Index)(iter.next());
286            oldsw = emitPackageIndexEntry(pkg, oldsw);
287        }
288    }
289
290    /**
291     * Emit an index entry for a package.
292     * Package names are unique, so no need to check for duplicates.
293     */
294    public char emitPackageIndexEntry(Index pkg, char oldsw) {
295        char res = oldsw;
296        // See if we are in a new section of the alphabet
297        char sw = pkg.name_.charAt(0);
298        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
299            // No need to emit section letters for packages
300            res = sw;
301            // Add the named anchor for this new letter
302            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
303        }
304        // Package names are unique, so no need to check for duplicates.
305        if (pkg.changeType_ == 0) {
306            h_.writeText("<A HREF=\"" + h_.reportFileName + "-summary" + h_.reportFileExt + "#" + pkg.name_  + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + pkg.name_ + "</strike></A><br>");
307        } else if (pkg.changeType_ == 1) {
308            h_.writeText("<A HREF=\"" + h_.reportFileName + "-summary" + h_.reportFileExt + "#" + pkg.name_  + "\" class=\"hiddenlink\" target=\"rightframe\"><b>" + pkg.name_ + "</b></A><br>");
309        } else if (pkg.changeType_ == 2) {
310            h_.writeText("<A HREF=\"pkg_" + pkg.name_ + h_.reportFileExt + "\" class=\"hiddenlink\" target=\"rightframe\">" + pkg.name_ + "</A><br>");
311        }
312        return res;
313    }
314
315    /**
316     * Emit all the entries and links for the given iterator
317     * to their respective files.
318     */
319    public void emitIndexEntries(Iterator iter) {
320        char oldsw = '\0';
321        int multipleMarker = 0;
322        Index currIndex = null; // The entry which is emitted
323        while (iter.hasNext()) {
324            // The next entry after the current one
325            Index nextIndex = (Index)(iter.next());
326            if (currIndex == null) {
327                currIndex = nextIndex; // Prime the pump
328            } else {
329                if (nextIndex.name_.compareTo(currIndex.name_) == 0) {
330                    // It's a duplicate index, so emit the name and then
331                    // the indented entries
332                    if (multipleMarker == 0)
333                        multipleMarker = 1; // Start of a duplicate index
334                    else if (multipleMarker == 1)
335                        multipleMarker = 2; // Inside a duplicate index
336                    oldsw = emitIndexEntry(currIndex, oldsw, multipleMarker);
337                } else {
338                    if (multipleMarker == 1)
339                        multipleMarker = 2; // Inside a duplicate index
340                    oldsw = emitIndexEntry(currIndex, oldsw, multipleMarker);
341                    multipleMarker = 0; // Not in a duplicate index any more
342                }
343                currIndex = nextIndex;
344            }
345        }
346        // Emit the last entry left in currIndex
347        if (multipleMarker == 1)
348            multipleMarker = 2; // Inside a duplicate index
349        if (currIndex != null)
350            oldsw = emitIndexEntry(currIndex, oldsw, multipleMarker);
351    }
352
353    /**
354     * Whether to log all missing @since tags to a file or not.
355     * If false, just warn the user.
356     */
357    public static boolean logMissingSinces = true;
358
359    /** The file used to output details of missing @since tags. */
360    public static PrintWriter missingSincesFile = null;
361
362    /**
363     * Emit elements in the given iterator which were added and
364     * missing @since tags.
365     */
366    public void emitMissingSinces(Iterator iter) {
367//        if (!logMissingSinces)
368//            return;
369        if (missingSincesFile == null) {
370            String sinceFileName = h_.outputDir + JDiff.DIR_SEP + "missingSinces.txt";
371            try {
372                FileOutputStream fos = new FileOutputStream(sinceFileName);
373                missingSincesFile = new PrintWriter(fos);
374            } catch (IOException e) {
375                System.out.println("IO Error while attempting to create " + sinceFileName);
376                System.out.println("Error: " + e.getMessage());
377                System.exit(1);
378            }
379        }
380        while (iter.hasNext()) {
381            Index currIndex = (Index)(iter.next());
382            // Only display information about added elements
383            if (currIndex.changeType_ != 1)
384                continue;
385            String programElementType = currIndex.ename_;
386            String details = null;
387            if (programElementType.compareTo("class") == 0) {
388                details = currIndex.pkgName_ + "." + currIndex.name_;
389                if (currIndex.isInterface_)
390                    details = details + " Interface";
391                else
392                    details = details + " Class";
393            } else if (programElementType.compareTo("constructor") == 0) {
394                details = currIndex.pkgName_ + "." + currIndex.name_ + " Constructor (" + currIndex.type_ + ")";
395            } else if (programElementType.compareTo("method") == 0) {
396                details = currIndex.pkgName_ + "." + currIndex.className_ + " " + "Method " + currIndex.name_ + "(" + currIndex.type_ + ")";
397            } else if (programElementType.compareTo("field") == 0) {
398                details = currIndex.pkgName_ + "." + currIndex.className_ + " " + "Field " + currIndex.name_;
399            } else {
400                System.out.println("Error: unknown program element type");
401                System.exit(3);
402            }
403            if (currIndex.doc_ == null) {
404                if (logMissingSinces)
405                    missingSincesFile.println("NO DOC BLOCK: " + details);
406                else
407                    System.out.println("Warning: the doc block for the new element: " + details + " is missing, so there is no @since tag");
408            } else if (currIndex.doc_.indexOf("@since") != -1) {
409                if (logMissingSinces)
410                    missingSincesFile.println("OK: " + details);
411            } else {
412                if (logMissingSinces)
413                    missingSincesFile.println("MISSING @SINCE TAG: " + details);
414                else
415                    System.out.println("Warning: the doc block for the new element: " + details + " is missing an @since tag");
416            }
417        }
418    }
419
420    /**
421     * Emit a single entry and the link to its file.
422     *
423     * @param programElementType "Class", "Constructor",
424     *                           "Method", or "Field".
425     */
426    public char emitIndexEntry(Index currIndex, char oldsw, int multipleMarker) {
427        String programElementType = currIndex.ename_;
428        if (programElementType.compareTo("class") == 0) {
429            return emitClassIndexEntry(currIndex, oldsw, multipleMarker);
430        } else if (programElementType.compareTo("constructor") == 0) {
431            return emitCtorIndexEntry(currIndex, oldsw, multipleMarker);
432        } else if (programElementType.compareTo("method") == 0) {
433            return emitMethodIndexEntry(currIndex, oldsw, multipleMarker);
434        } else if (programElementType.compareTo("field") == 0) {
435            return emitFieldIndexEntry(currIndex, oldsw, multipleMarker);
436        } else {
437            System.out.println("Error: unknown program element type");
438            System.exit(3);
439        }
440        return '\0';
441    }
442
443    /** Emit the index of classes, which appears in the bottom left frame. */
444    public void emitClassesIndex(APIDiff apiDiff, int indexType) {
445        // Add all the names of classes to a new list, to be sorted later
446        classNames = new ArrayList(); // Index[]
447        boolean hasRemovals = false;
448        boolean hasAdditions = false;
449        boolean hasChanges = false;
450        Iterator iter = apiDiff.packagesChanged.iterator();
451        while (iter.hasNext()) {
452            PackageDiff pkgDiff = (PackageDiff)(iter.next());
453            if (pkgDiff.classesRemoved.size() != 0)
454                hasRemovals = true;
455            if (pkgDiff.classesAdded.size() != 0)
456                hasAdditions = true;
457            if (pkgDiff.classesChanged.size() != 0)
458                hasChanges = true;
459            recordDiffs(hasRemovals, hasAdditions, hasChanges);
460            String pkgName = pkgDiff.name_;
461            Iterator iterClass = pkgDiff.classesRemoved.iterator();
462            while ((indexType == 3 || indexType == 0) && iterClass.hasNext()) {
463                ClassAPI cls = (ClassAPI)(iterClass.next());
464                classNames.add(new Index(cls.name_, 0, pkgName, cls.isInterface_));
465            }
466            iterClass = pkgDiff.classesAdded.iterator();
467            while ((indexType == 3 || indexType == 1) && iterClass.hasNext()) {
468                ClassAPI cls = (ClassAPI)(iterClass.next());
469                Index idx = new Index(cls.name_, 1, pkgName, cls.isInterface_);
470                idx.doc_ = cls.doc_; // Used for checking @since
471                classNames.add(idx);
472            }
473            iterClass = pkgDiff.classesChanged.iterator();
474            while ((indexType == 3 || indexType == 2) && iterClass.hasNext()) {
475                ClassDiff cls = (ClassDiff)(iterClass.next());
476                classNames.add(new Index(cls.name_, 2, pkgName, cls.isInterface_));
477            }
478        }
479        Collections.sort(classNames);
480        emitIndexHeader("Classes", indexType, hasRemovals, hasAdditions, hasChanges);
481        emitIndexEntries(classNames.iterator());
482        if (indexType == 1)
483            emitMissingSinces(classNames.iterator());
484    }
485
486    /** Emit an index entry for a class. */
487    public char emitClassIndexEntry(Index cls, char oldsw,
488                                    int multipleMarker) {
489        char res = oldsw;
490        String className = cls.pkgName_ + "." + cls.name_;
491        String classRef = cls.pkgName_ + "." + cls.name_;
492        boolean isInterface = cls.isInterface_;
493        // See if we are in a new section of the alphabet
494        char sw = cls.name_.charAt(0);
495        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
496            res = sw;
497            // Add the named anchor for this new letter
498            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
499            if (sw == '_')
500                h_.writeText("<br><b>underscore</b>&nbsp;");
501            else
502                h_.writeText("<br><font size=\"+2\">" + Character.toUpperCase(sw) + "</font>&nbsp;");
503            generateLetterIndex(classNames, sw, false);
504        }
505        // Deal with displaying duplicate indexes
506        if (multipleMarker == 1) {
507            h_.writeText("<i>" + cls.name_ + "</i><br>");
508        }
509        if (multipleMarker != 0)
510            h_.indent(INDENT_SIZE);
511        if (cls.changeType_ == 0) {
512            // Emit a reference to the correct place for the class in the
513            // JDiff page for the package
514            h_.writeText("<A HREF=\"pkg_" + cls.pkgName_ + h_.reportFileExt +
515                         "#" + cls.name_ + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + cls.name_ + "</strike></A><br>");
516        } else if (cls.changeType_ == 1) {
517            String cn = cls.name_;
518            if (multipleMarker != 0)
519                cn = cls.pkgName_;
520            if (isInterface)
521                h_.writeText("<A HREF=\"pkg_" + cls.pkgName_ + h_.reportFileExt + "#" + cls.name_ + "\" class=\"hiddenlink\" target=\"rightframe\"><b><i>" + cn + "</i></b></A><br>");
522            else
523                h_.writeText("<A HREF=\"pkg_" + cls.pkgName_ + h_.reportFileExt + "#" + cls.name_ + "\" class=\"hiddenlink\" target=\"rightframe\"><b>" + cn + "</b></A><br>");
524        } else if (cls.changeType_ == 2) {
525            String cn = cls.name_;
526            if (multipleMarker != 0)
527                cn = cls.pkgName_;
528            if (isInterface)
529                h_.writeText("<A HREF=\"" + classRef + h_.reportFileExt + "\" class=\"hiddenlink\" target=\"rightframe\"><i>" + cn + "</i></A><br>");
530            else
531                h_.writeText("<A HREF=\"" + classRef + h_.reportFileExt + "\" class=\"hiddenlink\" target=\"rightframe\">" + cn + "</A><br>");
532        }
533        return res;
534    }
535
536    /**
537     * Emit the index of all constructors, which appears in the bottom left
538     * frame.
539     */
540    public void emitConstructorsIndex(APIDiff apiDiff, int indexType) {
541        // Add all the names of constructors to a new list, to be sorted later
542        ctorNames = new ArrayList(); // Index[]
543        boolean hasRemovals = false;
544        boolean hasAdditions = false;
545        boolean hasChanges = false;
546        Iterator iter = apiDiff.packagesChanged.iterator();
547        while (iter.hasNext()) {
548            PackageDiff pkgDiff = (PackageDiff)(iter.next());
549            String pkgName = pkgDiff.name_;
550            Iterator iterClass = pkgDiff.classesChanged.iterator();
551            while (iterClass.hasNext()) {
552                ClassDiff classDiff = (ClassDiff)(iterClass.next());
553                if (classDiff.ctorsRemoved.size() != 0)
554                    hasRemovals = true;
555                if (classDiff.ctorsAdded.size() != 0)
556                    hasAdditions = true;
557                if (classDiff.ctorsChanged.size() != 0)
558                    hasChanges = true;
559                recordDiffs(hasRemovals, hasAdditions, hasChanges);
560                String className = classDiff.name_;
561                Iterator iterCtor = classDiff.ctorsRemoved.iterator();
562                while ((indexType == 3 || indexType == 0) && iterCtor.hasNext()) {
563                    ConstructorAPI ctor = (ConstructorAPI)(iterCtor.next());
564                    ctorNames.add(new Index(className, 0, pkgName, ctor.type_));
565                }
566                iterCtor = classDiff.ctorsAdded.iterator();
567                while ((indexType == 3 || indexType == 1) && iterCtor.hasNext()) {
568                    ConstructorAPI ctor = (ConstructorAPI)(iterCtor.next());
569                    Index idx = new Index(className, 1, pkgName, ctor.type_);
570                    idx.doc_ = ctor.doc_; // Used for checking @since
571                    ctorNames.add(idx);
572                }
573                iterCtor = classDiff.ctorsChanged.iterator();
574                while ((indexType == 3 || indexType == 2) && iterCtor.hasNext()) {
575                    MemberDiff ctor = (MemberDiff)(iterCtor.next());
576                    ctorNames.add(new Index(className, 2, pkgName, ctor.newType_));
577                }
578            }
579        }
580        Collections.sort(ctorNames);
581        emitIndexHeader("Constructors", indexType, hasRemovals, hasAdditions, hasChanges);
582        emitIndexEntries(ctorNames.iterator());
583        if (indexType == 1)
584            emitMissingSinces(ctorNames.iterator());
585    }
586
587    /** Emit an index entry for a constructor. */
588    public char emitCtorIndexEntry(Index ctor, char oldsw, int multipleMarker) {
589        char res = oldsw;
590        String className = ctor.pkgName_ + "." + ctor.name_;
591        String memberRef = ctor.pkgName_ + "." + ctor.name_;
592        String type = ctor.type_;
593        if (type.compareTo("void") == 0)
594            type = "";
595        String shownType = HTMLReportGenerator.simpleName(type);
596        // See if we are in a new section of the alphabet
597        char sw = ctor.name_.charAt(0);
598        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
599            res = sw;
600            // Add the named anchor for this new letter
601            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
602            if (sw == '_')
603                h_.writeText("<br><b>underscore</b>&nbsp;");
604            else
605                h_.writeText("<br><font size=\"+2\">" + Character.toUpperCase(sw) + "</font>&nbsp;");
606            generateLetterIndex(ctorNames, sw, false);
607        }
608        // Deal with displaying duplicate indexes
609        if (multipleMarker == 1) {
610            h_.writeText("<i>" + ctor.name_ + "</i><br>");
611        }
612        if (multipleMarker != 0)
613            h_.indent(INDENT_SIZE);
614        // Deal with each type of difference
615        // The output displayed for unique or duplicate entries is the same
616        // for constructors.
617        if (ctor.changeType_ == 0) {
618            String commentID = className + ".ctor_removed(" + type + ")";
619            h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + ctor.name_ + "</strike>");
620            h_.emitTypeWithParens(shownType, false);
621            h_.writeText("</A></nobr>&nbsp;constructor<br>");
622        } else if (ctor.changeType_ == 1) {
623            String commentID = className + ".ctor_added(" + type + ")";
624            h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><b>" + ctor.name_ + "</b>");
625            h_.emitTypeWithParens(shownType, false);
626            h_.writeText("</A></nobr>&nbsp;constructor<br>");
627        } else if (ctor.changeType_ == 2) {
628            String commentID = className + ".ctor_changed(" + type + ")";
629            h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + ctor.name_);
630            h_.emitTypeWithParens(shownType, false);
631            h_.writeText("</A></nobr>&nbsp;constructor<br>");
632        }
633        return res;
634    }
635
636    /**
637     * Emit the index of all methods, which appears in the bottom left frame.
638     */
639    public void emitMethodsIndex(APIDiff apiDiff, int indexType) {
640        // Add all the names of methods to a new list, to be sorted later
641        methNames = new ArrayList(); // Index[]
642        boolean hasRemovals = false;
643        boolean hasAdditions = false;
644        boolean hasChanges = false;
645        Iterator iter = apiDiff.packagesChanged.iterator();
646        while (iter.hasNext()) {
647            PackageDiff pkgDiff = (PackageDiff)(iter.next());
648            String pkgName = pkgDiff.name_;
649            Iterator iterClass = pkgDiff.classesChanged.iterator();
650            while (iterClass.hasNext()) {
651                ClassDiff classDiff = (ClassDiff)(iterClass.next());
652                if (classDiff.methodsRemoved.size() != 0)
653                    hasRemovals = true;
654                if (classDiff.methodsAdded.size() != 0)
655                    hasAdditions = true;
656                if (classDiff.methodsChanged.size() != 0)
657                    hasChanges = true;
658                recordDiffs(hasRemovals, hasAdditions, hasChanges);
659                String className = classDiff.name_;
660                Iterator iterMeth = classDiff.methodsRemoved.iterator();
661                while ((indexType == 3 || indexType == 0) && iterMeth.hasNext()) {
662                    MethodAPI meth = (MethodAPI)(iterMeth.next());
663                    methNames.add(new Index(meth.name_, 0, pkgName, className, meth.getSignature()));
664                }
665                iterMeth = classDiff.methodsAdded.iterator();
666                while ((indexType == 3 || indexType == 1) && iterMeth.hasNext()) {
667                    MethodAPI meth = (MethodAPI)(iterMeth.next());
668                    Index idx = new Index(meth.name_, 1, pkgName, className, meth.getSignature());
669                    idx.doc_ = meth.doc_; // Used for checking @since
670                    methNames.add(idx);
671                }
672                iterMeth = classDiff.methodsChanged.iterator();
673                while ((indexType == 3 || indexType == 2) && iterMeth.hasNext()) {
674                    MemberDiff meth = (MemberDiff)(iterMeth.next());
675                    methNames.add(new Index(meth.name_, 2, pkgName, className, meth.newSignature_));
676                }
677            }
678        }
679        Collections.sort(methNames);
680        emitIndexHeader("Methods", indexType, hasRemovals, hasAdditions, hasChanges);
681        emitIndexEntries(methNames.iterator());
682        if (indexType == 1)
683            emitMissingSinces(methNames.iterator());
684    }
685
686    /** Emit an index entry for a method. */
687    public char emitMethodIndexEntry(Index meth, char oldsw,
688                                     int multipleMarker) {
689        char res = oldsw;
690        String className = meth.pkgName_ + "." + meth.className_;
691        String memberRef = meth.pkgName_ + "." + meth.className_;
692        String type = meth.type_;
693        if (type.compareTo("void") == 0)
694            type = "";
695        String shownType = HTMLReportGenerator.simpleName(type);
696        // See if we are in a new section of the alphabet
697        char sw = meth.name_.charAt(0);
698        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
699            res = sw;
700            // Add the named anchor for this new letter
701            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
702            if (sw == '_')
703                h_.writeText("<br><b>underscore</b>&nbsp;");
704            else
705                h_.writeText("<br><font size=\"+2\">" + Character.toUpperCase(sw) + "</font>&nbsp;");
706            generateLetterIndex(methNames, sw, false);
707        }
708        // Deal with displaying duplicate indexes
709        if (multipleMarker == 1) {
710            h_.writeText("<i>" + meth.name_ + "</i><br>");
711        }
712        if (multipleMarker != 0)
713            h_.indent(INDENT_SIZE);
714        // Deal with each type of difference
715        if (meth.changeType_ == 0) {
716            String commentID = className + "." + meth.name_ + "_removed(" + type + ")";
717            if (multipleMarker == 0) {
718                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + meth.name_ + "</strike>");
719                h_.emitTypeWithParens(shownType, false);
720            } else {
721                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">type&nbsp;<strike>");
722                h_.emitTypeWithParens(shownType, false);
723                h_.writeText("</strike>&nbsp;in&nbsp;" + className);
724            }
725            h_.writeText("</A></nobr><br>");
726        } else if (meth.changeType_ == 1) {
727            String commentID = className + "." + meth.name_ + "_added(" + type + ")";
728            if (multipleMarker == 0) {
729                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><b>" + meth.name_ + "</b>");
730                h_.emitTypeWithParens(shownType, false);
731            } else {
732                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">type&nbsp;<b>");
733                h_.emitTypeWithParens(shownType, false);
734                h_.writeText("</b>&nbsp;in&nbsp;" + className);
735            }
736            h_.writeText("</A></nobr><br>");
737        } else if (meth.changeType_ == 2) {
738            String commentID = className + "." + meth.name_ + "_changed(" + type + ")";
739            if (multipleMarker == 0) {
740                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + meth.name_);
741                h_.emitTypeWithParens(shownType, false);
742            } else {
743                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">type&nbsp;");
744                h_.emitTypeWithParens(shownType, false);
745                h_.writeText("&nbsp;in&nbsp;" + className);
746            }
747            h_.writeText("</A></nobr><br>");
748        }
749        return res;
750    }
751
752    /**
753     * Emit the index of all fields, which appears in the bottom left frame.
754     */
755    public void emitFieldsIndex(APIDiff apiDiff, int indexType) {
756        // Add all the names of fields to a new list, to be sorted later
757        fieldNames = new ArrayList(); // Index[]
758        boolean hasRemovals = false;
759        boolean hasAdditions = false;
760        boolean hasChanges = false;
761        Iterator iter = apiDiff.packagesChanged.iterator();
762        while (iter.hasNext()) {
763            PackageDiff pkgDiff = (PackageDiff)(iter.next());
764            String pkgName = pkgDiff.name_;
765            Iterator iterClass = pkgDiff.classesChanged.iterator();
766            while (iterClass.hasNext()) {
767                ClassDiff classDiff = (ClassDiff)(iterClass.next());
768                if (classDiff.fieldsRemoved.size() != 0)
769                    hasRemovals = true;
770                if (classDiff.fieldsAdded.size() != 0)
771                    hasAdditions = true;
772                if (classDiff.fieldsChanged.size() != 0)
773                    hasChanges = true;
774                recordDiffs(hasRemovals, hasAdditions, hasChanges);
775                String className = classDiff.name_;
776                Iterator iterField = classDiff.fieldsRemoved.iterator();
777                while ((indexType == 3 || indexType == 0) && iterField.hasNext()) {
778                    FieldAPI fld = (FieldAPI)(iterField.next());
779                    fieldNames.add(new Index(fld.name_, 0, pkgName, className, fld.type_, true));
780                }
781                iterField = classDiff.fieldsAdded.iterator();
782                while ((indexType == 3 || indexType == 1) && iterField.hasNext()) {
783                    FieldAPI fld = (FieldAPI)(iterField.next());
784                    Index idx = new Index(fld.name_, 1, pkgName, className, fld.type_, true);
785                    idx.doc_ = fld.doc_; // Used for checking @since
786                    fieldNames.add(idx);
787                }
788                iterField = classDiff.fieldsChanged.iterator();
789                while ((indexType == 3 || indexType == 2) && iterField.hasNext()) {
790                    MemberDiff fld = (MemberDiff)(iterField.next());
791                    fieldNames.add(new Index(fld.name_, 2, pkgName, className, fld.newType_, true));
792                }
793            }
794        }
795        Collections.sort(fieldNames);
796        emitIndexHeader("Fields", indexType, hasRemovals, hasAdditions, hasChanges);
797        emitIndexEntries(fieldNames.iterator());
798        if (indexType == 1)
799            emitMissingSinces(fieldNames.iterator());
800    }
801
802    /** Emit an index entry for a field. */
803    public char emitFieldIndexEntry(Index fld, char oldsw,
804                                    int multipleMarker) {
805        char res = oldsw;
806        String className = fld.pkgName_ + "." + fld.className_;
807        String memberRef = fld.pkgName_ + "." + fld.className_;
808        String type = fld.type_;
809        if (type.compareTo("void") == 0)
810            type = "";
811        String shownType = HTMLReportGenerator.simpleName(type);
812        // See if we are in a new section of the alphabet
813        char sw = fld.name_.charAt(0);
814        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
815            res = sw;
816            // Add the named anchor for this new letter
817            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
818            if (sw == '_')
819                h_.writeText("<br><b>underscore</b>&nbsp;");
820            else
821                h_.writeText("<br><font size=\"+2\">" + Character.toUpperCase(sw) + "</font>&nbsp;");
822            generateLetterIndex(fieldNames, sw, false);
823        }
824        // Deal with displaying duplicate indexes
825        if (multipleMarker == 1) {
826            h_.writeText("<i>" + fld.name_ + "</i><br>");
827        }
828        if (multipleMarker != 0) {
829// More context than this is helpful here: h_.indent(INDENT_SIZE);
830            h_.writeText("&nbsp;in&nbsp;");
831        }
832        // Deal with each type of difference
833        if (fld.changeType_ == 0) {
834            String commentID = className + "." + fld.name_;
835            if (multipleMarker == 0) {
836                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + fld.name_ + "</strike></A>");
837                h_.writeText("</nobr><br>");
838            } else {
839                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + className + "</strike></A>");
840                h_.writeText("</nobr><br>");
841            }
842        } else if (fld.changeType_ == 1) {
843            String commentID = className + "." + fld.name_;
844            if (multipleMarker == 0) {
845                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + fld.name_ + "</A>");
846                h_.writeText("</nobr><br>");
847            } else {
848                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + className + "</A>");
849                h_.writeText("</nobr><br>");
850            }
851        } else if (fld.changeType_ == 2) {
852            String commentID = className + "." + fld.name_;
853            if (multipleMarker == 0) {
854                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + fld.name_ + "</A>");
855                h_.writeText("</nobr><br>");
856            } else {
857                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + className + "</A>");
858                h_.writeText("</nobr><br>");
859            }
860        }
861        return res;
862    }
863
864    /**
865     * Emit the index of all changes, which appears in the bottom left frame.
866     * Has to be run after all the other indexes have been written, since it
867     * uses data from when they are generated.
868     */
869    public void emitAllDiffsIndex(APIDiff apiDiff, int indexType) {
870        allNames = new ArrayList(); // Index[]
871        // Add all the changes into one big list, and sort it by name,
872        // ignoring case
873        allNames.addAll(packageNames);
874        allNames.addAll(classNames);
875        allNames.addAll(ctorNames);
876        allNames.addAll(methNames);
877        allNames.addAll(fieldNames);
878        // Compares two Index objects' names, ignoring case differences.
879        Collections.sort(allNames);
880
881        emitIndexHeader("All Differences", indexType, atLeastOneRemoval,
882                        atLeastOneAddition, atLeastOneChange);
883
884        // Tell generateLetterIndex to use allNames as the list when
885        // using the other methods to generate the indexes.
886        isAllNames = true;
887
888        // Now emit a line for each entry in the list in the appropriate
889        // format for each program element
890        Iterator iter = allNames.iterator();
891        char oldsw = '\0';
892        int multipleMarker = 0;
893        Index currIndex = null; // The entry which is emitted
894        while (iter.hasNext()) {
895            // The next entry after the current one
896            Index nextIndex = (Index)(iter.next());
897            if (currIndex == null) {
898                currIndex = nextIndex; // Prime the pump
899            } else {
900                if (nextIndex.name_.compareTo(currIndex.name_) == 0) {
901                    // It's a duplicate index, so emit the name and then
902                    // the indented entries
903                    if (multipleMarker == 0)
904                        multipleMarker = 1; // Start of a duplicate index
905                    else if (multipleMarker == 1)
906                        multipleMarker = 2; // Inside a duplicate index
907                    oldsw = emitIndexEntryForAny(currIndex, oldsw, multipleMarker);
908                } else {
909                    if (multipleMarker == 1)
910                        multipleMarker = 2; // Inside a duplicate index
911                    oldsw = emitIndexEntryForAny(currIndex, oldsw, multipleMarker);
912                    multipleMarker = 0; // Not in a duplicate index any more
913                }
914                currIndex = nextIndex;
915            }
916        }
917        // Emit the last entry left in currIndex
918        if (multipleMarker == 1)
919            multipleMarker = 2; // Inside a duplicate index
920        if (currIndex != null)
921            oldsw = emitIndexEntryForAny(currIndex, oldsw, multipleMarker);
922
923        // Tell generateLetterIndex to stop using allNames as the list when
924        // using the other methods to generate the indexes.
925        isAllNames = false;
926    }
927
928    /** Call the appropriate *IndexEntry method for each entry. */
929    public char emitIndexEntryForAny(Index currIndex, char oldsw,
930                                     int multipleMarker) {
931        if (currIndex.ename_.compareTo("package") == 0) {
932            h_.writeText("<!-- Package " + currIndex.name_ + " -->");
933            return emitPackageIndexEntry(currIndex, oldsw);
934        } else if (currIndex.ename_.compareTo("class") == 0) {
935            h_.writeText("<!-- Class " + currIndex.name_ + " -->");
936            return emitClassIndexEntry(currIndex, oldsw, multipleMarker);
937        } else if (currIndex.ename_.compareTo("constructor") == 0) {
938            h_.writeText("<!-- Constructor " + currIndex.name_ + " -->");
939            return emitCtorIndexEntry(currIndex, oldsw, multipleMarker);
940        } else if (currIndex.ename_.compareTo("method") == 0) {
941            h_.writeText("<!-- Method " + currIndex.name_ + " -->");
942            return emitMethodIndexEntry(currIndex, oldsw, multipleMarker);
943        } else if (currIndex.ename_.compareTo("field") == 0) {
944            h_.writeText("<!-- Field " + currIndex.name_ + " -->");
945            return emitFieldIndexEntry(currIndex, oldsw, multipleMarker);
946        }
947        return '\0';
948    }
949
950    /** The list of all changes for all program elements. */
951    private List allNames = null; // Index[]
952
953    /** The list of all package changes. */
954    private List packageNames = null; // Index[]
955
956    /** The list of all class changes. */
957    private List classNames = null; // Index[]
958
959    /** The list of all constructor changes. */
960    private List ctorNames = null; // Index[]
961
962    /** The list of all method changes. */
963    private List methNames = null; // Index[]
964
965    /** The list of all field changes. */
966    private List fieldNames = null; // Index[]
967
968    /** If set, then use allNames to generate the letter indexes. */
969    private boolean isAllNames = false;
970
971    /**
972     * If any of the parameters are set, then set the respective atLeastOne
973     * variable, used to generate the links at the top of the allDiffs index.
974     * Never unset an atLeastOne variable.
975     */
976    private void recordDiffs(boolean hasRemovals, boolean hasAdditions,
977                        boolean hasChanges) {
978        if (hasRemovals)
979            atLeastOneRemoval = true;
980        if (hasAdditions)
981            atLeastOneAddition = true;
982        if (hasChanges)
983            atLeastOneChange = true;
984    }
985
986    /** Set if there was at least one removal in the entire API. */
987    private boolean atLeastOneRemoval = false;
988
989    /** Set if there was at least one addition in the entire API. */
990    private boolean atLeastOneAddition = false;
991
992    /** Set if there was at least one change in the entire API. */
993    private boolean atLeastOneChange = false;
994
995    /**
996     * The number of non-breaking spaces to indent a duplicate indexes'
997     * entries by.
998     */
999    private final int INDENT_SIZE = 2;
1000}
1001
1002/**
1003 * Class used to produce indexes of packages and classes.
1004 *
1005 * See the file LICENSE.txt for copyright details.
1006 * @author Matthew Doar, mdoar@pobox.com
1007 */
1008class Index implements Comparable {
1009
1010    /** The name of the program element this Index object represents. */
1011    public String ename_ = null;
1012
1013    /** Name of the changed package, class or member. */
1014    public String name_ = null;
1015
1016    /** Type of change. 0 = remove, 1 = add, 2 = change. */
1017    public int changeType_;
1018
1019    /** Name of the changed package if name_ is a class name. */
1020    public String pkgName_ = null;
1021
1022    /** Set if this class is an interface. */
1023    public boolean isInterface_= false;
1024
1025    /** The doc block of added elements, default is null. */
1026    public String doc_ = null;
1027
1028    /**
1029     * The new member type. For methods, this is the signature.
1030     */
1031    public String type_ = null;
1032
1033    /**
1034     * The class name. Only used by methods.
1035     */
1036    public String className_ = null;
1037
1038    /** Constructor for packages. */
1039    public Index(String name, int changeType) {
1040        ename_ = "package";
1041        name_ = name;
1042        changeType_ = changeType;
1043    }
1044
1045    /** Constructor for classes. */
1046    public Index(String name, int changeType, String pkgName, boolean isInterface) {
1047        ename_ = "class";
1048        name_ = name;
1049        changeType_ = changeType;
1050        pkgName_ = pkgName;
1051        isInterface_ = isInterface;
1052    }
1053
1054    /** Constructor for constructors. */
1055    public Index(String name, int changeType, String pkgName, String type) {
1056        ename_ = "constructor";
1057        name_ = name;
1058        changeType_ = changeType;
1059        pkgName_ = pkgName;
1060        type_  = type;
1061    }
1062
1063    /** Constructor for methods. */
1064    public Index(String name, int changeType, String pkgName,
1065                 String className, String type) {
1066        ename_ = "method";
1067        name_ = name;
1068        changeType_ = changeType;
1069        pkgName_ = pkgName;
1070        className_ = className;
1071        type_  = type;
1072    }
1073
1074    /**
1075     * Constructor for fields.
1076     *
1077     * The boolean <code>fld</code> is simply there to differentiate this
1078     * constructor from the one for methods.
1079     */
1080    public Index(String name, int changeType, String pkgName,
1081                 String className, String type, boolean fld) {
1082        ename_ = "field";
1083        name_ = name;
1084        changeType_ = changeType;
1085        pkgName_ = pkgName;
1086        className_ = className;
1087        type_  = type;
1088    }
1089
1090
1091    /** Compare two Index objects by their simple names, ignoring case. */
1092    public int compareTo(Object o) {
1093        return name_.compareToIgnoreCase(((Index)o).name_);
1094    }
1095
1096}
1097
1098