FileDocCategorySizeDatePackage
HTMLIndexes.javaAPI DocAndroid 1.5 API51043Wed May 06 22:41:22 BST 2009jdiff

HTMLIndexes

public class HTMLIndexes extends Object
Emit HTML indexes which appear in the bottom left frame in the report. All indexes are links to JDiff-generated pages. See the file LICENSE.txt for copyright details.
author
Matthew Doar, mdoar@pobox.com

Fields Summary
private HTMLReportGenerator
h_
The HTMLReportGenerator instance used to write HTML.
public static boolean
logMissingSinces
Whether to log all missing @since tags to a file or not. If false, just warn the user.
public static PrintWriter
missingSincesFile
The file used to output details of missing @since tags.
private List
allNames
The list of all changes for all program elements.
private List
packageNames
The list of all package changes.
private List
classNames
The list of all class changes.
private List
ctorNames
The list of all constructor changes.
private List
methNames
The list of all method changes.
private List
fieldNames
The list of all field changes.
private boolean
isAllNames
If set, then use allNames to generate the letter indexes.
private boolean
atLeastOneRemoval
Set if there was at least one removal in the entire API.
private boolean
atLeastOneAddition
Set if there was at least one addition in the entire API.
private boolean
atLeastOneChange
Set if there was at least one change in the entire API.
private final int
INDENT_SIZE
The number of non-breaking spaces to indent a duplicate indexes' entries by.
Constructors Summary
public HTMLIndexes(HTMLReportGenerator h)
Constructor.

        h_ = h;
    
Methods Summary
public voidemitAllBottomLeftFiles(java.lang.String packagesIndexName, java.lang.String classesIndexName, java.lang.String constructorsIndexName, java.lang.String methodsIndexName, java.lang.String fieldsIndexName, java.lang.String allDiffsIndexName, APIDiff apiDiff)
Emit all the bottom left frame index files.

    
             
        
                                         
                                         
                                        
                                         
                                         
                                         
        
        // indexType values: 0 = removals only, 1 = additions only, 
        // 2 = changes only. 3 = all differences. Run all differences
        // first for all program element types so we know whether there
        // are any removals etc for the allDiffs index.
        emitBottomLeftFile(packagesIndexName, apiDiff, 3, "Package");
        emitBottomLeftFile(classesIndexName, apiDiff, 3, "Class");
        emitBottomLeftFile(constructorsIndexName, apiDiff, 3, "Constructor");
        emitBottomLeftFile(methodsIndexName, apiDiff, 3, "Method");
        emitBottomLeftFile(fieldsIndexName, apiDiff, 3, "Field");
        // The allindex must be done last, since it uses the results from 
        // the previous ones
        emitBottomLeftFile(allDiffsIndexName, apiDiff, 3, "All");
        // Now generate the other indexes
        for (int indexType = 0; indexType < 3; indexType++) {
            emitBottomLeftFile(packagesIndexName, apiDiff, indexType, "Package");
            emitBottomLeftFile(classesIndexName, apiDiff, indexType, "Class");
            emitBottomLeftFile(constructorsIndexName, apiDiff, indexType, "Constructor");
            emitBottomLeftFile(methodsIndexName, apiDiff, indexType, "Method");
            emitBottomLeftFile(fieldsIndexName, apiDiff, indexType, "Field");
            emitBottomLeftFile(allDiffsIndexName, apiDiff, indexType, "All");
        }
        if (missingSincesFile != null)
            missingSincesFile.close();
    
public voidemitAllDiffsIndex(APIDiff apiDiff, int indexType)
Emit the index of all changes, which appears in the bottom left frame. Has to be run after all the other indexes have been written, since it uses data from when they are generated.

        allNames = new ArrayList(); // Index[]
        // Add all the changes into one big list, and sort it by name,
        // ignoring case
        allNames.addAll(packageNames);
        allNames.addAll(classNames);
        allNames.addAll(ctorNames);
        allNames.addAll(methNames);
        allNames.addAll(fieldNames);
        // Compares two Index objects' names, ignoring case differences.
        Collections.sort(allNames);

        emitIndexHeader("All Differences", indexType, atLeastOneRemoval, 
                        atLeastOneAddition, atLeastOneChange);

        // Tell generateLetterIndex to use allNames as the list when 
        // using the other methods to generate the indexes.
        isAllNames = true; 
        
        // Now emit a line for each entry in the list in the appropriate 
        // format for each program element
        Iterator iter = allNames.iterator();
        char oldsw = '\0";
        int multipleMarker = 0;
        Index currIndex = null; // The entry which is emitted
        while (iter.hasNext()) {
            // The next entry after the current one
            Index nextIndex = (Index)(iter.next()); 
            if (currIndex == null) {
                currIndex = nextIndex; // Prime the pump
            } else {
                if (nextIndex.name_.compareTo(currIndex.name_) == 0) {
                    // It's a duplicate index, so emit the name and then
                    // the indented entries
                    if (multipleMarker == 0)
                        multipleMarker = 1; // Start of a duplicate index
                    else if (multipleMarker == 1)
                        multipleMarker = 2; // Inside a duplicate index
                    oldsw = emitIndexEntryForAny(currIndex, oldsw, multipleMarker);
                } else {
                    if (multipleMarker == 1)
                        multipleMarker = 2; // Inside a duplicate index
                    oldsw = emitIndexEntryForAny(currIndex, oldsw, multipleMarker);
                    multipleMarker = 0; // Not in a duplicate index any more
                }
                currIndex = nextIndex;
            }
        }
        // Emit the last entry left in currIndex
        if (multipleMarker == 1)
            multipleMarker = 2; // Inside a duplicate index
        if (currIndex != null)
            oldsw = emitIndexEntryForAny(currIndex, oldsw, multipleMarker);

        // Tell generateLetterIndex to stop using allNames as the list when 
        // using the other methods to generate the indexes.
        isAllNames = false; 
    
public voidemitBottomLeftFile(java.lang.String indexBaseName, APIDiff apiDiff, int indexType, java.lang.String programElementType)
Emit a single bottom left frame with the given kind of differences for the given program element type in an alphabetical index.

param
indexBaseName The base name of the index file.
param
apiDiff The root element containing all the API differences.
param
indexType 0 = removals only, 1 = additions only, 2 = changes only, 3 = all differences,
param
programElementType "Package", "Class", "Constructor", "Method", "Field" or "All".

        String filename = indexBaseName;
        try {
            String title = "Indexes"; 
            if (indexType == 0) {
                filename += "_removals" + h_.reportFileExt;
                title = programElementType + " Removals Index";
            } else if (indexType == 1) {
                filename += "_additions" + h_.reportFileExt;
                title = programElementType + " Additions Index";
            } else if (indexType == 2) {
                filename += "_changes" + h_.reportFileExt;
                title = programElementType + " Changes Index";
            } else if (indexType == 3) {
                filename += "_all" + h_.reportFileExt;
                title = programElementType + " Differences Index";
            }
                
            FileOutputStream fos = new FileOutputStream(filename);
            h_.reportFile = new PrintWriter(fos);
            h_.writeStartHTMLHeader();
            h_.writeHTMLTitle(title);
            h_.writeStyleSheetRef();
            h_.writeText("</HEAD>");
            h_.writeText("<BODY>");
            
            if (programElementType.compareTo("Package") == 0) {
                emitPackagesIndex(apiDiff, indexType);
            } else if (programElementType.compareTo("Class") == 0) {
                emitClassesIndex(apiDiff, indexType);
            } else if (programElementType.compareTo("Constructor") == 0) {
                emitConstructorsIndex(apiDiff, indexType);
            } else if (programElementType.compareTo("Method") == 0) {
                emitMethodsIndex(apiDiff, indexType);
            } else if (programElementType.compareTo("Field") == 0) {
                emitFieldsIndex(apiDiff, indexType);
            } else if (programElementType.compareTo("All") == 0) {
                emitAllDiffsIndex(apiDiff, indexType);
            } else{
                System.out.println("Error: unknown program element type.");
                System.exit(3);
            }
            
            h_.writeHTMLFooter();
            h_.reportFile.close();
        } catch(IOException e) {
            System.out.println("IO Error while attempting to create " + filename);
            System.out.println("Error: " + e.getMessage());
            System.exit(1);
        }
    
public charemitClassIndexEntry(jdiff.Index cls, char oldsw, int multipleMarker)
Emit an index entry for a class.

        char res = oldsw;
        String className = cls.pkgName_ + "." + cls.name_;
        String classRef = cls.pkgName_ + "." + cls.name_;
        boolean isInterface = cls.isInterface_;
        // See if we are in a new section of the alphabet
        char sw = cls.name_.charAt(0);
        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
            res = sw;
            // Add the named anchor for this new letter
            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
            if (sw == '_")
                h_.writeText("<br><b>underscore</b> ");
            else
                h_.writeText("<br><font size=\"+2\">" + Character.toUpperCase(sw) + "</font> ");
            generateLetterIndex(classNames, sw, false);
        }
        // Deal with displaying duplicate indexes
        if (multipleMarker == 1) {
            h_.writeText("<i>" + cls.name_ + "</i><br>");
        }
        if (multipleMarker != 0)
            h_.indent(INDENT_SIZE);
        if (cls.changeType_ == 0) {
            // Emit a reference to the correct place for the class in the 
            // JDiff page for the package
            h_.writeText("<A HREF=\"pkg_" + cls.pkgName_ + h_.reportFileExt + 
                         "#" + cls.name_ + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + cls.name_ + "</strike></A><br>");
        } else if (cls.changeType_ == 1) {
            String cn = cls.name_;
            if (multipleMarker != 0)
                cn = cls.pkgName_;
            if (isInterface)
                h_.writeText("<A HREF=\"pkg_" + cls.pkgName_ + h_.reportFileExt + "#" + cls.name_ + "\" class=\"hiddenlink\" target=\"rightframe\"><b><i>" + cn + "</i></b></A><br>");
            else
                h_.writeText("<A HREF=\"pkg_" + cls.pkgName_ + h_.reportFileExt + "#" + cls.name_ + "\" class=\"hiddenlink\" target=\"rightframe\"><b>" + cn + "</b></A><br>");
        } else if (cls.changeType_ == 2) {
            String cn = cls.name_;
            if (multipleMarker != 0)
                cn = cls.pkgName_;
            if (isInterface)
                h_.writeText("<A HREF=\"" + classRef + h_.reportFileExt + "\" class=\"hiddenlink\" target=\"rightframe\"><i>" + cn + "</i></A><br>");
            else
                h_.writeText("<A HREF=\"" + classRef + h_.reportFileExt + "\" class=\"hiddenlink\" target=\"rightframe\">" + cn + "</A><br>");
        }
        return res;
    
public voidemitClassesIndex(APIDiff apiDiff, int indexType)
Emit the index of classes, which appears in the bottom left frame.

        // Add all the names of classes to a new list, to be sorted later
        classNames = new ArrayList(); // Index[]
        boolean hasRemovals = false;
        boolean hasAdditions = false;
        boolean hasChanges = false;
        Iterator iter = apiDiff.packagesChanged.iterator();
        while (iter.hasNext()) {
            PackageDiff pkgDiff = (PackageDiff)(iter.next());
            if (pkgDiff.classesRemoved.size() != 0)
                hasRemovals = true;
            if (pkgDiff.classesAdded.size() != 0)
                hasAdditions = true;
            if (pkgDiff.classesChanged.size() != 0)
                hasChanges = true;
            recordDiffs(hasRemovals, hasAdditions, hasChanges);
            String pkgName = pkgDiff.name_;
            Iterator iterClass = pkgDiff.classesRemoved.iterator();
            while ((indexType == 3 || indexType == 0) && iterClass.hasNext()) {
                ClassAPI cls = (ClassAPI)(iterClass.next());
                classNames.add(new Index(cls.name_, 0, pkgName, cls.isInterface_));
            }
            iterClass = pkgDiff.classesAdded.iterator();
            while ((indexType == 3 || indexType == 1) && iterClass.hasNext()) {
                ClassAPI cls = (ClassAPI)(iterClass.next());
                Index idx = new Index(cls.name_, 1, pkgName, cls.isInterface_);
                idx.doc_ = cls.doc_; // Used for checking @since
                classNames.add(idx);
            }
            iterClass = pkgDiff.classesChanged.iterator();
            while ((indexType == 3 || indexType == 2) && iterClass.hasNext()) {
                ClassDiff cls = (ClassDiff)(iterClass.next());
                classNames.add(new Index(cls.name_, 2, pkgName, cls.isInterface_));
            }
        }
        Collections.sort(classNames);
        emitIndexHeader("Classes", indexType, hasRemovals, hasAdditions, hasChanges);
        emitIndexEntries(classNames.iterator());
        if (indexType == 1)
            emitMissingSinces(classNames.iterator());
    
public voidemitConstructorsIndex(APIDiff apiDiff, int indexType)
Emit the index of all constructors, which appears in the bottom left frame.

        // Add all the names of constructors to a new list, to be sorted later
        ctorNames = new ArrayList(); // Index[]
        boolean hasRemovals = false;
        boolean hasAdditions = false;
        boolean hasChanges = false;
        Iterator iter = apiDiff.packagesChanged.iterator();
        while (iter.hasNext()) {
            PackageDiff pkgDiff = (PackageDiff)(iter.next());
            String pkgName = pkgDiff.name_;
            Iterator iterClass = pkgDiff.classesChanged.iterator();
            while (iterClass.hasNext()) {
                ClassDiff classDiff = (ClassDiff)(iterClass.next());
                if (classDiff.ctorsRemoved.size() != 0)
                    hasRemovals = true;
                if (classDiff.ctorsAdded.size() != 0)
                    hasAdditions = true;
                if (classDiff.ctorsChanged.size() != 0)
                    hasChanges = true;
                recordDiffs(hasRemovals, hasAdditions, hasChanges);
                String className = classDiff.name_;
                Iterator iterCtor = classDiff.ctorsRemoved.iterator();
                while ((indexType == 3 || indexType == 0) && iterCtor.hasNext()) {
                    ConstructorAPI ctor = (ConstructorAPI)(iterCtor.next());
                    ctorNames.add(new Index(className, 0, pkgName, ctor.type_));
                }
                iterCtor = classDiff.ctorsAdded.iterator();
                while ((indexType == 3 || indexType == 1) && iterCtor.hasNext()) {
                    ConstructorAPI ctor = (ConstructorAPI)(iterCtor.next());
                    Index idx = new Index(className, 1, pkgName, ctor.type_);
                    idx.doc_ = ctor.doc_; // Used for checking @since
                    ctorNames.add(idx);
                }
                iterCtor = classDiff.ctorsChanged.iterator();
                while ((indexType == 3 || indexType == 2) && iterCtor.hasNext()) {
                    MemberDiff ctor = (MemberDiff)(iterCtor.next());
                    ctorNames.add(new Index(className, 2, pkgName, ctor.newType_));
                }
            }
        }
        Collections.sort(ctorNames);
        emitIndexHeader("Constructors", indexType, hasRemovals, hasAdditions, hasChanges);
        emitIndexEntries(ctorNames.iterator());
        if (indexType == 1)
            emitMissingSinces(ctorNames.iterator());
    
public charemitCtorIndexEntry(jdiff.Index ctor, char oldsw, int multipleMarker)
Emit an index entry for a constructor.

        char res = oldsw;
        String className = ctor.pkgName_ + "." + ctor.name_;
        String memberRef = ctor.pkgName_ + "." + ctor.name_;
        String type = ctor.type_;
        if (type.compareTo("void") == 0)
            type = "";
        String shownType = HTMLReportGenerator.simpleName(type);
        // See if we are in a new section of the alphabet
        char sw = ctor.name_.charAt(0);
        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
            res = sw;
            // Add the named anchor for this new letter
            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
            if (sw == '_")
                h_.writeText("<br><b>underscore</b> ");
            else
                h_.writeText("<br><font size=\"+2\">" + Character.toUpperCase(sw) + "</font> ");
            generateLetterIndex(ctorNames, sw, false);
        }
        // Deal with displaying duplicate indexes
        if (multipleMarker == 1) {
            h_.writeText("<i>" + ctor.name_ + "</i><br>");
        }
        if (multipleMarker != 0)
            h_.indent(INDENT_SIZE);
        // Deal with each type of difference
        // The output displayed for unique or duplicate entries is the same
        // for constructors.
        if (ctor.changeType_ == 0) {
            String commentID = className + ".ctor_removed(" + type + ")";
            h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + ctor.name_ + "</strike>");
            h_.emitTypeWithParens(shownType, false);
            h_.writeText("</A></nobr> constructor<br>");
        } else if (ctor.changeType_ == 1) {
            String commentID = className + ".ctor_added(" + type + ")";
            h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><b>" + ctor.name_ + "</b>");
            h_.emitTypeWithParens(shownType, false);
            h_.writeText("</A></nobr> constructor<br>");
        } else if (ctor.changeType_ == 2) {
            String commentID = className + ".ctor_changed(" + type + ")";
            h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + ctor.name_);
            h_.emitTypeWithParens(shownType, false);
            h_.writeText("</A></nobr> constructor<br>");
        }
        return res;
    
public charemitFieldIndexEntry(jdiff.Index fld, char oldsw, int multipleMarker)
Emit an index entry for a field.

        char res = oldsw;
        String className = fld.pkgName_ + "." + fld.className_;
        String memberRef = fld.pkgName_ + "." + fld.className_;
        String type = fld.type_;
        if (type.compareTo("void") == 0)
            type = "";
        String shownType = HTMLReportGenerator.simpleName(type);
        // See if we are in a new section of the alphabet
        char sw = fld.name_.charAt(0);
        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
            res = sw;
            // Add the named anchor for this new letter
            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
            if (sw == '_")
                h_.writeText("<br><b>underscore</b> ");
            else
                h_.writeText("<br><font size=\"+2\">" + Character.toUpperCase(sw) + "</font> ");
            generateLetterIndex(fieldNames, sw, false);
        }
        // Deal with displaying duplicate indexes
        if (multipleMarker == 1) {
            h_.writeText("<i>" + fld.name_ + "</i><br>");
        }
        if (multipleMarker != 0) {
// More context than this is helpful here: h_.indent(INDENT_SIZE);
            h_.writeText(" in ");
        }
        // Deal with each type of difference
        if (fld.changeType_ == 0) {
            String commentID = className + "." + fld.name_;                    
            if (multipleMarker == 0) {            
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + fld.name_ + "</strike></A>");
                h_.writeText("</nobr><br>");
            } else {
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + className + "</strike></A>");
                h_.writeText("</nobr><br>");
            }
        } else if (fld.changeType_ == 1) {
            String commentID = className + "." + fld.name_;                    
            if (multipleMarker == 0) {            
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + fld.name_ + "</A>");
                h_.writeText("</nobr><br>");
            } else {
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + className + "</A>");
                h_.writeText("</nobr><br>");
            }
        } else if (fld.changeType_ == 2) {
            String commentID = className + "." + fld.name_;                    
            if (multipleMarker == 0) {            
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + fld.name_ + "</A>");
                h_.writeText("</nobr><br>");
            } else {
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + className + "</A>");
                h_.writeText("</nobr><br>");
            }
        }
        return res;
    
public voidemitFieldsIndex(APIDiff apiDiff, int indexType)
Emit the index of all fields, which appears in the bottom left frame.

        // Add all the names of fields to a new list, to be sorted later
        fieldNames = new ArrayList(); // Index[]
        boolean hasRemovals = false;
        boolean hasAdditions = false;
        boolean hasChanges = false;
        Iterator iter = apiDiff.packagesChanged.iterator();
        while (iter.hasNext()) {
            PackageDiff pkgDiff = (PackageDiff)(iter.next());
            String pkgName = pkgDiff.name_;
            Iterator iterClass = pkgDiff.classesChanged.iterator();
            while (iterClass.hasNext()) {
                ClassDiff classDiff = (ClassDiff)(iterClass.next());
                if (classDiff.fieldsRemoved.size() != 0)
                    hasRemovals = true;
                if (classDiff.fieldsAdded.size() != 0)
                    hasAdditions = true;
                if (classDiff.fieldsChanged.size() != 0)
                    hasChanges = true;
                recordDiffs(hasRemovals, hasAdditions, hasChanges);
                String className = classDiff.name_;
                Iterator iterField = classDiff.fieldsRemoved.iterator();
                while ((indexType == 3 || indexType == 0) && iterField.hasNext()) {
                    FieldAPI fld = (FieldAPI)(iterField.next());
                    fieldNames.add(new Index(fld.name_, 0, pkgName, className, fld.type_, true));
                }
                iterField = classDiff.fieldsAdded.iterator();
                while ((indexType == 3 || indexType == 1) && iterField.hasNext()) {
                    FieldAPI fld = (FieldAPI)(iterField.next());
                    Index idx = new Index(fld.name_, 1, pkgName, className, fld.type_, true);
                    idx.doc_ = fld.doc_; // Used for checking @since
                    fieldNames.add(idx);
                }
                iterField = classDiff.fieldsChanged.iterator();
                while ((indexType == 3 || indexType == 2) && iterField.hasNext()) {
                    MemberDiff fld = (MemberDiff)(iterField.next());
                    fieldNames.add(new Index(fld.name_, 2, pkgName, className, fld.newType_, true));
                }
            }
        }
        Collections.sort(fieldNames);
        emitIndexHeader("Fields", indexType, hasRemovals, hasAdditions, hasChanges);
        emitIndexEntries(fieldNames.iterator());
        if (indexType == 1)
            emitMissingSinces(fieldNames.iterator());
    
public voidemitIndexEntries(java.util.Iterator iter)
Emit all the entries and links for the given iterator to their respective files.

        char oldsw = '\0";
        int multipleMarker = 0;
        Index currIndex = null; // The entry which is emitted
        while (iter.hasNext()) {
            // The next entry after the current one
            Index nextIndex = (Index)(iter.next()); 
            if (currIndex == null) {
                currIndex = nextIndex; // Prime the pump
            } else {
                if (nextIndex.name_.compareTo(currIndex.name_) == 0) {
                    // It's a duplicate index, so emit the name and then
                    // the indented entries
                    if (multipleMarker == 0)
                        multipleMarker = 1; // Start of a duplicate index
                    else if (multipleMarker == 1)
                        multipleMarker = 2; // Inside a duplicate index
                    oldsw = emitIndexEntry(currIndex, oldsw, multipleMarker);
                } else {
                    if (multipleMarker == 1)
                        multipleMarker = 2; // Inside a duplicate index
                    oldsw = emitIndexEntry(currIndex, oldsw, multipleMarker);
                    multipleMarker = 0; // Not in a duplicate index any more
                }
                currIndex = nextIndex;
            }
        }
        // Emit the last entry left in currIndex
        if (multipleMarker == 1)
            multipleMarker = 2; // Inside a duplicate index
        if (currIndex != null)
            oldsw = emitIndexEntry(currIndex, oldsw, multipleMarker);
    
public charemitIndexEntry(jdiff.Index currIndex, char oldsw, int multipleMarker)
Emit a single entry and the link to its file.

param
programElementType "Class", "Constructor", "Method", or "Field".

        String programElementType = currIndex.ename_;
        if (programElementType.compareTo("class") == 0) {
            return emitClassIndexEntry(currIndex, oldsw, multipleMarker);
        } else if (programElementType.compareTo("constructor") == 0) {
            return emitCtorIndexEntry(currIndex, oldsw, multipleMarker);
        } else if (programElementType.compareTo("method") == 0) {
            return emitMethodIndexEntry(currIndex, oldsw, multipleMarker);
        } else if (programElementType.compareTo("field") == 0) {
            return emitFieldIndexEntry(currIndex, oldsw, multipleMarker);
        } else {
            System.out.println("Error: unknown program element type");
            System.exit(3);
        }
        return '\0";
    
public charemitIndexEntryForAny(jdiff.Index currIndex, char oldsw, int multipleMarker)
Call the appropriate *IndexEntry method for each entry.

        if (currIndex.ename_.compareTo("package") == 0) {
            h_.writeText("<!-- Package " + currIndex.name_ + " -->");
            return emitPackageIndexEntry(currIndex, oldsw);
        } else if (currIndex.ename_.compareTo("class") == 0) {
            h_.writeText("<!-- Class " + currIndex.name_ + " -->");
            return emitClassIndexEntry(currIndex, oldsw, multipleMarker);
        } else if (currIndex.ename_.compareTo("constructor") == 0) {
            h_.writeText("<!-- Constructor " + currIndex.name_ + " -->");
            return emitCtorIndexEntry(currIndex, oldsw, multipleMarker);
        } else if (currIndex.ename_.compareTo("method") == 0) {
            h_.writeText("<!-- Method " + currIndex.name_ + " -->");
            return emitMethodIndexEntry(currIndex, oldsw, multipleMarker);
        } else if (currIndex.ename_.compareTo("field") == 0) {
            h_.writeText("<!-- Field " + currIndex.name_ + " -->");
            return emitFieldIndexEntry(currIndex, oldsw, multipleMarker);
        }
        return '\0";
    
private voidemitIndexHeader(java.lang.String indexName, int indexType, boolean hasRemovals, boolean hasAdditions, boolean hasChanges)
Emit a header for an index, including suitable links for removed, added and changes sub-indexes.

        String linkIndexName = indexName.toLowerCase();
        boolean isAllDiffs = false;
        if (indexName.compareTo("All Differences") == 0) {
            linkIndexName = "alldiffs";
            isAllDiffs = true;
        }
        h_.writeText("<a NAME=\"topheader\"></a>"); // Named anchor
        h_.writeText("<table summary=\"Index for " +  indexName + "\" width=\"100%\" class=\"index\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
        h_.writeText("  <tr>");
        h_.writeText("  <th class=\"indexHeader\">");
        h_.writeText("    Filter the Index:");
        h_.writeText("  </th>");
        h_.writeText("  </tr>");
        h_.writeText("  <tr>");
        h_.writeText("  <td class=\"indexText\" style=\"line-height:1.5em;padding-left:2em;\">");
//        h_.writeText("  <div style=\"line-height:1.25em;padding-left:1em;>\">");
//        h_.writeText("  <FONT SIZE=\"-1\">");
        // The index name is also a hidden link to the *index_all page
        if (indexType == 3) {
             h_.writeText("<b>" + indexName + "</b>"); }
        else if (isAllDiffs) {
            h_.writeText("<a href=\"" + linkIndexName + "_index_all" + h_.reportFileExt + "\" class=\"hiddenlink\">" + indexName + "</a>");
        }
        else {
            h_.writeText("<a href=\"" + linkIndexName + "_index_all" + h_.reportFileExt + "\" class=\"staysblack\">All " + indexName + "</a>");
        }
//        h_.writeText("  </FONT>");

        h_.writeText("  <br>");
//        h_.writeText("  <FONT SIZE=\"-1\">");
        if (hasRemovals) {
          if (indexType == 0) {
            h_.writeText("<b>Removals</b>");
          } else {
            h_.writeText("<A HREF=\"" + linkIndexName + "_index_removals" + h_.reportFileExt + "\" class=\"hiddenlink\">Removals</A>");
          }
        } else {
            h_.writeText("<font color=\"#999999\">Removals</font>");
        }
//        h_.writeText("  </FONT>");

        h_.writeText("  <br>");
//      h_.writeText("  <FONT SIZE=\"-1\">");
        if (hasAdditions) {
          if (indexType == 1) {
            h_.writeText("<b>Additions</b>");
          } else {
            h_.writeText("<A HREF=\"" + linkIndexName + "_index_additions" + h_.reportFileExt + "\"class=\"hiddenlink\">Additions</A>");
          }
        } else {
            h_.writeText("<font color=\"#999999\">Additions</font>");
        }
//        h_.writeText("  </FONT>");

        h_.writeText("  <br>");
//         h_.writeText("  <FONT SIZE=\"-1\">");
        if (hasChanges) {
          if (indexType == 2) {
            h_.writeText("<b>Changes</b>");
          } else {
            h_.writeText("<A HREF=\"" + linkIndexName + "_index_changes" + h_.reportFileExt + "\"class=\"hiddenlink\">Changes</A>");
          }
        } else {
            h_.writeText("<font color=\"#999999\">Changes</font>");
        }
//        h_.writeText("  </FONT>");
//        h_.writeText("  </div>");
        h_.writeText("  </td>");
        h_.writeText("  </tr>");
        h_.writeText("</table>");
        h_.writeText("<font size=\"-2\"><strong>Bold</strong> indicates New; <strike>Strike</strike> indicates deleted</font>");
        h_.writeText("  </br>");

    
public charemitMethodIndexEntry(jdiff.Index meth, char oldsw, int multipleMarker)
Emit an index entry for a method.

        char res = oldsw;
        String className = meth.pkgName_ + "." + meth.className_;
        String memberRef = meth.pkgName_ + "." + meth.className_;
        String type = meth.type_;
        if (type.compareTo("void") == 0)
            type = "";
        String shownType = HTMLReportGenerator.simpleName(type);
        // See if we are in a new section of the alphabet
        char sw = meth.name_.charAt(0);
        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
            res = sw;
            // Add the named anchor for this new letter
            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
            if (sw == '_")
                h_.writeText("<br><b>underscore</b> ");
            else
                h_.writeText("<br><font size=\"+2\">" + Character.toUpperCase(sw) + "</font> ");
            generateLetterIndex(methNames, sw, false);
        }
        // Deal with displaying duplicate indexes
        if (multipleMarker == 1) {
            h_.writeText("<i>" + meth.name_ + "</i><br>");
        }
        if (multipleMarker != 0)
            h_.indent(INDENT_SIZE);
        // Deal with each type of difference
        if (meth.changeType_ == 0) {
            String commentID = className + "." + meth.name_ + "_removed(" + type + ")";                    
            if (multipleMarker == 0) {
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + meth.name_ + "</strike>");
                h_.emitTypeWithParens(shownType, false);
            } else {
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">type <strike>");
                h_.emitTypeWithParens(shownType, false);
                h_.writeText("</strike> in " + className);
            }
            h_.writeText("</A></nobr><br>");
        } else if (meth.changeType_ == 1) {
            String commentID = className + "." + meth.name_ + "_added(" + type + ")";                    
            if (multipleMarker == 0) {
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\"><b>" + meth.name_ + "</b>");
                h_.emitTypeWithParens(shownType, false);
            } else {
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">type <b>");
                h_.emitTypeWithParens(shownType, false);
                h_.writeText("</b> in " + className);
            }
            h_.writeText("</A></nobr><br>");
        } else if (meth.changeType_ == 2) {
            String commentID = className + "." + meth.name_ + "_changed(" + type + ")";                    
            if (multipleMarker == 0) {
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">" + meth.name_);
                h_.emitTypeWithParens(shownType, false);
            } else {
                h_.writeText("<nobr><A HREF=\"" + memberRef + h_.reportFileExt + "#" + commentID + "\" class=\"hiddenlink\" target=\"rightframe\">type ");
                h_.emitTypeWithParens(shownType, false);
                h_.writeText(" in " + className);
            }
            h_.writeText("</A></nobr><br>");
        }
        return res;
    
public voidemitMethodsIndex(APIDiff apiDiff, int indexType)
Emit the index of all methods, which appears in the bottom left frame.

        // Add all the names of methods to a new list, to be sorted later
        methNames = new ArrayList(); // Index[]
        boolean hasRemovals = false;
        boolean hasAdditions = false;
        boolean hasChanges = false;
        Iterator iter = apiDiff.packagesChanged.iterator();
        while (iter.hasNext()) {
            PackageDiff pkgDiff = (PackageDiff)(iter.next());
            String pkgName = pkgDiff.name_;
            Iterator iterClass = pkgDiff.classesChanged.iterator();
            while (iterClass.hasNext()) {
                ClassDiff classDiff = (ClassDiff)(iterClass.next());
                if (classDiff.methodsRemoved.size() != 0)
                    hasRemovals = true;
                if (classDiff.methodsAdded.size() != 0)
                    hasAdditions = true;
                if (classDiff.methodsChanged.size() != 0)
                    hasChanges = true;
                recordDiffs(hasRemovals, hasAdditions, hasChanges);
                String className = classDiff.name_;
                Iterator iterMeth = classDiff.methodsRemoved.iterator();
                while ((indexType == 3 || indexType == 0) && iterMeth.hasNext()) {
                    MethodAPI meth = (MethodAPI)(iterMeth.next());
                    methNames.add(new Index(meth.name_, 0, pkgName, className, meth.getSignature()));
                }
                iterMeth = classDiff.methodsAdded.iterator();
                while ((indexType == 3 || indexType == 1) && iterMeth.hasNext()) {
                    MethodAPI meth = (MethodAPI)(iterMeth.next());
                    Index idx = new Index(meth.name_, 1, pkgName, className, meth.getSignature());
                    idx.doc_ = meth.doc_; // Used for checking @since
                    methNames.add(idx);
                }
                iterMeth = classDiff.methodsChanged.iterator();
                while ((indexType == 3 || indexType == 2) && iterMeth.hasNext()) {
                    MemberDiff meth = (MemberDiff)(iterMeth.next());
                    methNames.add(new Index(meth.name_, 2, pkgName, className, meth.newSignature_));
                }
            }
        }
        Collections.sort(methNames);
        emitIndexHeader("Methods", indexType, hasRemovals, hasAdditions, hasChanges);
        emitIndexEntries(methNames.iterator());
        if (indexType == 1)
            emitMissingSinces(methNames.iterator());
    
public voidemitMissingSinces(java.util.Iterator iter)
Emit elements in the given iterator which were added and missing @since tags.


                         
        
//        if (!logMissingSinces)
//            return;
        if (missingSincesFile == null) {
            String sinceFileName = h_.outputDir + JDiff.DIR_SEP + "missingSinces.txt";
            try {
                FileOutputStream fos = new FileOutputStream(sinceFileName);
                missingSincesFile = new PrintWriter(fos);
            } catch (IOException e) {
                System.out.println("IO Error while attempting to create " + sinceFileName);
                System.out.println("Error: " + e.getMessage());
                System.exit(1);
            }
        }
        while (iter.hasNext()) {
            Index currIndex = (Index)(iter.next()); 
            // Only display information about added elements
            if (currIndex.changeType_ != 1) 
                continue;
            String programElementType = currIndex.ename_;
            String details = null;
            if (programElementType.compareTo("class") == 0) {
                details = currIndex.pkgName_ + "." + currIndex.name_;
                if (currIndex.isInterface_)
                    details = details + " Interface";
                else
                    details = details + " Class";
            } else if (programElementType.compareTo("constructor") == 0) {
                details = currIndex.pkgName_ + "." + currIndex.name_ + " Constructor (" + currIndex.type_ + ")";
            } else if (programElementType.compareTo("method") == 0) {
                details = currIndex.pkgName_ + "." + currIndex.className_ + " " + "Method " + currIndex.name_ + "(" + currIndex.type_ + ")";
            } else if (programElementType.compareTo("field") == 0) {
                details = currIndex.pkgName_ + "." + currIndex.className_ + " " + "Field " + currIndex.name_;
            } else {
                System.out.println("Error: unknown program element type");
                System.exit(3);
            }
            if (currIndex.doc_ == null) {
                if (logMissingSinces)
                    missingSincesFile.println("NO DOC BLOCK: " + details);
                else
                    System.out.println("Warning: the doc block for the new element: " + details + " is missing, so there is no @since tag");
            } else if (currIndex.doc_.indexOf("@since") != -1) {
                if (logMissingSinces)
                    missingSincesFile.println("OK: " + details);
            } else {
                if (logMissingSinces)
                    missingSincesFile.println("MISSING @SINCE TAG: " + details);
                else
                    System.out.println("Warning: the doc block for the new element: " + details + " is missing an @since tag");
            }
        }
    
public charemitPackageIndexEntry(jdiff.Index pkg, char oldsw)
Emit an index entry for a package. Package names are unique, so no need to check for duplicates.

        char res = oldsw;
        // See if we are in a new section of the alphabet
        char sw = pkg.name_.charAt(0);
        if (Character.toUpperCase(sw) != Character.toUpperCase(oldsw)) {
            // No need to emit section letters for packages
            res = sw;
            // Add the named anchor for this new letter
            h_.writeText("<A NAME=\"" + Character.toUpperCase(res) + "\"></A>");
        }
        // Package names are unique, so no need to check for duplicates.
        if (pkg.changeType_ == 0) {
            h_.writeText("<A HREF=\"" + h_.reportFileName + "-summary" + h_.reportFileExt + "#" + pkg.name_  + "\" class=\"hiddenlink\" target=\"rightframe\"><strike>" + pkg.name_ + "</strike></A><br>");
        } else if (pkg.changeType_ == 1) {
            h_.writeText("<A HREF=\"" + h_.reportFileName + "-summary" + h_.reportFileExt + "#" + pkg.name_  + "\" class=\"hiddenlink\" target=\"rightframe\"><b>" + pkg.name_ + "</b></A><br>");
        } else if (pkg.changeType_ == 2) {
            h_.writeText("<A HREF=\"pkg_" + pkg.name_ + h_.reportFileExt + "\" class=\"hiddenlink\" target=\"rightframe\">" + pkg.name_ + "</A><br>");
        }
        return res;
    
public voidemitPackagesIndex(APIDiff apiDiff, int indexType)
Emit the index of packages, which appears in the bottom left frame.

        // Add all the names of packages to a new list, to be sorted later
        packageNames = new ArrayList(); // Index[]
        boolean hasRemovals = false;
        if (apiDiff.packagesRemoved.size() != 0)
            hasRemovals = true;
        boolean hasAdditions = false;
        if (apiDiff.packagesAdded.size() != 0)
            hasAdditions = true;
        boolean hasChanges = false;
        if (apiDiff.packagesChanged.size() != 0)
            hasChanges = true;
        recordDiffs(hasRemovals, hasAdditions, hasChanges);
        Iterator iter = apiDiff.packagesRemoved.iterator();
        while ((indexType == 3 || indexType == 0) && iter.hasNext()) {
            PackageAPI pkg = (PackageAPI)(iter.next());
            packageNames.add(new Index(pkg.name_, 0));
        }
        iter = apiDiff.packagesAdded.iterator();
        while ((indexType == 3 || indexType == 1) && iter.hasNext()) {
            PackageAPI pkg = (PackageAPI)(iter.next());
            packageNames.add(new Index(pkg.name_, 1));
        }
        iter = apiDiff.packagesChanged.iterator();
        while ((indexType == 3 || indexType == 2) && iter.hasNext()) {
            PackageDiff pkg = (PackageDiff)(iter.next());
            packageNames.add(new Index(pkg.name_, 2));
        }
        Collections.sort(packageNames);

        // No letter index needed for packages

        // Now emit all the package names and links to their respective files
        emitIndexHeader("Packages", indexType, hasRemovals, hasAdditions, hasChanges);

        // Extra line because no index is emitted
        h_.writeText("<br>");

        // Package names are unique, so no need to check for duplicates.
        iter = packageNames.iterator();
        char oldsw = '\0";
        while (iter.hasNext()) {
            Index pkg = (Index)(iter.next());
            oldsw = emitPackageIndexEntry(pkg, oldsw);
        }
    
private voidgenerateLetterIndex(java.util.List list, char currChar, boolean larger)
Generate a small header of letters which link to each section, but do not emit a linked letter for the current section. Finish the list off with a link to the top of the index. Caching the results of this function would save about 10s with large APIs.

        if (larger)
            return; // Currently not using the larger functionality
        int size = -2;
	if (larger)
            size = -1;
        Iterator iter = null;
        if (isAllNames)
            iter = allNames.iterator();
        else
            iter = list.iterator();
        char oldsw = '\0";
        while (iter.hasNext()) {
            Index entry = (Index)(iter.next());
            char sw = entry.name_.charAt(0);
            char swu = Character.toUpperCase(sw);
            if (swu != Character.toUpperCase(oldsw)) {
                // Don't emit a reference to the current letter
                if (Character.toUpperCase(sw) != Character.toUpperCase(currChar)) {
                    if (swu == '_") {
                        h_.writeText("<a href=\"#" + swu + "\"><font size=\"" + size + "\">" + "underscore" + "</font></a> ");
                    } else {
                        h_.writeText("<a href=\"#" + swu + "\"><font size=\"" + size + "\">" + swu + "</font></a> ");
                    }
                }
                oldsw = sw;
            }
        }
        h_.writeText(" <a href=\"#topheader\"><font size=\"" + size + "\">TOP</font></a>");
        h_.writeText("<p><div style=\"line-height:1.5em;color:black\">");
    
private voidrecordDiffs(boolean hasRemovals, boolean hasAdditions, boolean hasChanges)
If any of the parameters are set, then set the respective atLeastOne variable, used to generate the links at the top of the allDiffs index. Never unset an atLeastOne variable.


                                         
          
                          
        if (hasRemovals)
            atLeastOneRemoval = true;
        if (hasAdditions)
            atLeastOneAddition = true;
        if (hasChanges)
            atLeastOneChange = true;