package jdiff; import java.util.*; import java.io.*; import java.text.*; /** * Emit HTML based on the changes between two sets of APIs. * * See the file LICENSE.txt for copyright details. * @author Matthew Doar, mdoar@pobox.com */ public class HTMLReportGenerator { /** Default constructor. */ public HTMLReportGenerator() { } /** The Comments object for existing comments. */ private Comments existingComments_ = null; /** * The Comments object for freshly regenerated comments. * This is populated during the generation of the report, * and should be like existingComments_ but with unused comments * marked as such, so that they can be commented out in XML when * the new comments are written out to the comments file. */ private Comments newComments_ = null; /** * Accessor method for the freshly generated Comments object. * The list of comments is sorted before the object is returned. */ public Comments getNewComments() { Collections.sort(newComments_.commentsList_); return newComments_; } /** Generate the report. */ public void generate(APIComparator comp, Comments existingComments) { String fullReportFileName = reportFileName; if (outputDir != null) fullReportFileName = outputDir + JDiff.DIR_SEP + reportFileName; System.out.println("JDiff: generating HTML report into the file '" + fullReportFileName + reportFileExt + "' and the subdirectory '" + fullReportFileName + "'"); // May be null if no comments file exists yet existingComments_ = existingComments; // Where the new comments will be placed newComments_ = new Comments(); // Writing to multiple files, so make sure the subdirectory exists File opdir = new File(fullReportFileName); if (!opdir.mkdir() && !opdir.exists()) { System.out.println("Error: could not create the subdirectory '" + fullReportFileName + "'"); System.exit(3); } // Emit the documentation difference files if (!Diff.noDocDiffs) { // Documentation differences, one file per package Diff.emitDocDiffs(fullReportFileName); } // This is the top-level summary file, first in the right hand frame // or linked at the start to if no frames are used. String changesSummaryName = fullReportFileName + JDiff.DIR_SEP + reportFileName + "-summary" + reportFileExt; apiDiff = comp.apiDiff; try { FileOutputStream fos = new FileOutputStream(changesSummaryName); reportFile = new PrintWriter(fos); writeStartHTMLHeader(); // Write out the title in he HTML header String oldAPIName = "Old API"; if (apiDiff.oldAPIName_ != null) oldAPIName = apiDiff.oldAPIName_; String newAPIName = "New API"; if (apiDiff.newAPIName_ != null) newAPIName = apiDiff.newAPIName_; if (windowTitle == null) writeHTMLTitle("Android API Differences Report"); else writeHTMLTitle(windowTitle); writeStyleSheetRef(); writeText(""); writeText(""); // writeText("
"); // Add the nav bar for the summary page writeNavigationBar(reportFileName + "-summary", null, null, null, 0, true, apiDiff.packagesRemoved.size() != 0, apiDiff.packagesAdded.size() != 0, apiDiff.packagesChanged.size() != 0); // Write the title in the body with some formatting if (docTitle == null) { //writeText("
"); writeText("
"); writeText("

API Differences Report

"); writeText("
"); } else { writeText("
"); writeText("

" + docTitle + "

"); writeText("
"); } writeText("

This document details the changes in the Android framework API. It shows "); writeText("additions, modifications, and removals for packages, classes, methods, and "); writeText("fields. Each reference to an API change includes a brief description of the "); writeText("API and an explanation of the change and suggested workaround, where available.

"); writeText("

The differences described in this report are based a comparison of the APIs "); writeText("whose versions are specified in the upper-right corner of this page. It compares a "); writeText("newer \"to\" API to an older \"from\" version, noting any changes relative to the "); writeText("older API. So, for example, indicated API removals are no longer present in the \"to\" "); writeText("API.

"); writeText("

To navigate the report, use the \"Select a Diffs Index\" and \"Filter the Index\" "); writeText("controls on the left. The report uses text formatting to indicate interface names, "); writeText("links to reference documentation, and links to change "); writeText("description.

"); writeText("

For more information about the Android framework API and SDK, "); writeText("see the Android product site.

"); // Write the contents and the other files as well writeReport(apiDiff); writeHTMLFooter(); reportFile.close(); } catch(IOException e) { System.out.println("IO Error while attempting to create " + changesSummaryName); System.out.println("Error: " + e.getMessage()); System.exit(1); } // Now generate all the other files for multiple frames. // // The top-level changes.html frames file where everything starts. String tln = fullReportFileName + reportFileExt; // The file for the top-left frame. String tlf = fullReportFileName + JDiff.DIR_SEP + "jdiff_topleftframe" + reportFileExt; // The default file for the bottom-left frame is the one with the // most information in it. String allDiffsIndexName = fullReportFileName + JDiff.DIR_SEP + "alldiffs_index"; // Other indexes for the bottom-left frame. String packagesIndexName = fullReportFileName + JDiff.DIR_SEP + "packages_index"; String classesIndexName = fullReportFileName + JDiff.DIR_SEP + "classes_index"; String constructorsIndexName = fullReportFileName + JDiff.DIR_SEP + "constructors_index"; String methodsIndexName = fullReportFileName + JDiff.DIR_SEP + "methods_index"; String fieldsIndexName = fullReportFileName + JDiff.DIR_SEP + "fields_index"; HTMLFiles hf = new HTMLFiles(this); hf.emitTopLevelFile(tln, apiDiff); hf.emitTopLeftFile(tlf); hf.emitHelp(fullReportFileName, apiDiff); hf.emitStylesheet(); HTMLIndexes h = new HTMLIndexes(this); h.emitAllBottomLeftFiles(packagesIndexName, classesIndexName, constructorsIndexName, methodsIndexName, fieldsIndexName, allDiffsIndexName, apiDiff); if (doStats) { // The file for the statistical report. String sf = fullReportFileName + JDiff.DIR_SEP + "jdiff_statistics" + reportFileExt; HTMLStatistics stats = new HTMLStatistics(this); stats.emitStatistics(sf, apiDiff); } } /** * Write the HTML report. * * The top section describes all the packages added (with links) and * removed, and the changed packages section has links which takes you * to a section for each package. This pattern continues for classes and * constructors, methods and fields. */ public void writeReport(APIDiff apiDiff) { // Report packages which were removed in the new API if (apiDiff.packagesRemoved.size() != 0) { writeTableStart("Removed Packages", 2); Iterator iter = apiDiff.packagesRemoved.iterator(); while (iter.hasNext()) { PackageAPI pkgAPI = (PackageAPI)(iter.next()); String pkgName = pkgAPI.name_; if (trace) System.out.println("Package " + pkgName + " was removed."); writePackageTableEntry(pkgName, 0, pkgAPI.doc_, false); } writeTableEnd(); } // Report packages which were added in the new API if (apiDiff.packagesAdded.size() != 0) { writeTableStart("Added Packages", 2); Iterator iter = apiDiff.packagesAdded.iterator(); while (iter.hasNext()) { PackageAPI pkgAPI = (PackageAPI)(iter.next()); String pkgName = pkgAPI.name_; if (trace) System.out.println("Package " + pkgName + " was added."); writePackageTableEntry(pkgName, 1, pkgAPI.doc_, false); } writeTableEnd(); } // Report packages which were changed in the new API if (apiDiff.packagesChanged.size() != 0) { // Emit a table of changed packages, with links to the file // for each package. writeTableStart("Changed Packages", 3); Iterator iter = apiDiff.packagesChanged.iterator(); while (iter.hasNext()) { PackageDiff pkgDiff = (PackageDiff)(iter.next()); String pkgName = pkgDiff.name_; if (trace) System.out.println("Package " + pkgName + " was changed."); writePackageTableEntry(pkgName, 2, null, false); } writeTableEnd(); writeText(""); // Now emit a separate file for each changed package. writeText(""); PackageDiff[] pkgDiffs = new PackageDiff[apiDiff.packagesChanged.size()]; pkgDiffs = (PackageDiff[])apiDiff.packagesChanged.toArray(pkgDiffs); for (int i = 0; i < pkgDiffs.length; i++) { reportChangedPackage(pkgDiffs, i); } } writeText("
"); writeText(""); writeText("
"); writeText(" "); writeText(" "); writeText("
"); writeText(" ©2008 Google - "); writeText(" Code Home - "); writeText(" Site Terms of Service - "); writeText(" Privacy Policy "); writeText(" "); writeText("
"); writeText(" Generated by JDiff
"); writeText(" "); writeText("
"); writeText("
"); writeText(""); } /** * Write out the details of a changed package in a separate file. */ public void reportChangedPackage(PackageDiff[] pkgDiffs, int pkgIndex) { PackageDiff pkgDiff = pkgDiffs[pkgIndex]; String pkgName = pkgDiff.name_; PrintWriter oldReportFile = null; oldReportFile = reportFile; String localReportFileName = null; try { // Prefix package files with pkg_ because there may be a class // with the same name. localReportFileName = reportFileName + JDiff.DIR_SEP + "pkg_" + pkgName + reportFileExt; if (outputDir != null) localReportFileName = outputDir + JDiff.DIR_SEP + localReportFileName; FileOutputStream fos = new FileOutputStream(localReportFileName); reportFile = new PrintWriter(fos); writeStartHTMLHeader(); writeHTMLTitle(pkgName); writeStyleSheetRef(); writeText(""); writeText(""); } catch(IOException e) { System.out.println("IO Error while attempting to create " + localReportFileName); System.out.println("Error: "+ e.getMessage()); System.exit(1); } String pkgRef = pkgName; pkgRef = pkgRef.replace('.', '/'); pkgRef = newDocPrefix + pkgRef + "/package-summary"; // A link to the package in the new API String linkedPkgName = "" + pkgName + ""; String prevPkgRef = null; if (pkgIndex != 0) { prevPkgRef = "pkg_" + pkgDiffs[pkgIndex-1].name_ + reportFileExt; } // Create the HTML link to the next package String nextPkgRef = null; if (pkgIndex < pkgDiffs.length - 1) { nextPkgRef = "pkg_" + pkgDiffs[pkgIndex+1].name_ + reportFileExt; } writeSectionHeader("Package " + linkedPkgName, pkgName, prevPkgRef, nextPkgRef, null, 1, pkgDiff.classesRemoved.size() != 0, pkgDiff.classesAdded.size() != 0, pkgDiff.classesChanged.size() != 0); // Report changes in documentation if (reportDocChanges && pkgDiff.documentationChange_ != null) { String pkgDocRef = pkgName + "/package-summary"; pkgDocRef = pkgDocRef.replace('.', '/'); String oldPkgRef = pkgDocRef; String newPkgRef = pkgDocRef; if (oldDocPrefix != null) oldPkgRef = oldDocPrefix + oldPkgRef; else oldPkgRef = null; newPkgRef = newDocPrefix + newPkgRef; if (oldPkgRef != null) pkgDiff.documentationChange_ += "old to "; else pkgDiff.documentationChange_ += "old to "; pkgDiff.documentationChange_ += "new. "; writeText(pkgDiff.documentationChange_); } // Report classes which were removed in the new API if (pkgDiff.classesRemoved.size() != 0) { // Determine the title for this section boolean hasClasses = false; boolean hasInterfaces = false; Iterator iter = pkgDiff.classesRemoved.iterator(); while (iter.hasNext()) { ClassAPI classAPI = (ClassAPI)(iter.next()); if (classAPI.isInterface_) hasInterfaces = true; else hasClasses = true; } if (hasInterfaces && hasClasses) writeTableStart("Removed Classes and Interfaces", 2); else if (!hasInterfaces && hasClasses) writeTableStart("Removed Classes", 2); else if (hasInterfaces && !hasClasses) writeTableStart("Removed Interfaces", 2); // Emit the table entries iter = pkgDiff.classesRemoved.iterator(); while (iter.hasNext()) { ClassAPI classAPI = (ClassAPI)(iter.next()); String className = classAPI.name_; if (trace) System.out.println("Class/Interface " + className + " was removed."); writeClassTableEntry(pkgName, className, 0, classAPI.isInterface_, classAPI.doc_, false); } writeTableEnd(); } // Report classes which were added in the new API if (pkgDiff.classesAdded.size() != 0) { // Determine the title for this section boolean hasClasses = false; boolean hasInterfaces = false; Iterator iter = pkgDiff.classesAdded.iterator(); while (iter.hasNext()) { ClassAPI classAPI = (ClassAPI)(iter.next()); if (classAPI.isInterface_) hasInterfaces = true; else hasClasses = true; } if (hasInterfaces && hasClasses) writeTableStart("Added Classes and Interfaces", 2); else if (!hasInterfaces && hasClasses) writeTableStart("Added Classes", 2); else if (hasInterfaces && !hasClasses) writeTableStart("Added Interfaces", 2); // Emit the table entries iter = pkgDiff.classesAdded.iterator(); while (iter.hasNext()) { ClassAPI classAPI = (ClassAPI)(iter.next()); String className = classAPI.name_; if (trace) System.out.println("Class/Interface " + className + " was added."); writeClassTableEntry(pkgName, className, 1, classAPI.isInterface_, classAPI.doc_, false); } writeTableEnd(); } // Report classes which were changed in the new API if (pkgDiff.classesChanged.size() != 0) { // Determine the title for this section boolean hasClasses = false; boolean hasInterfaces = false; Iterator iter = pkgDiff.classesChanged.iterator(); while (iter.hasNext()) { ClassDiff classDiff = (ClassDiff)(iter.next()); if (classDiff.isInterface_) hasInterfaces = true; else hasClasses = true; } if (hasInterfaces && hasClasses) writeTableStart("Changed Classes and Interfaces", 2); else if (!hasInterfaces && hasClasses) writeTableStart("Changed Classes", 2); else if (hasInterfaces && !hasClasses) writeTableStart("Changed Interfaces", 2); // Emit a table of changed classes, with links to the file // for each class. iter = pkgDiff.classesChanged.iterator(); while (iter.hasNext()) { ClassDiff classDiff = (ClassDiff)(iter.next()); String className = classDiff.name_; if (trace) System.out.println("Package " + pkgDiff.name_ + ", class/Interface " + className + " was changed."); writeClassTableEntry(pkgName, className, 2, classDiff.isInterface_, null, false); } writeTableEnd(); // Now emit a separate file for each changed class and interface. ClassDiff[] classDiffs = new ClassDiff[pkgDiff.classesChanged.size()]; classDiffs = (ClassDiff[])pkgDiff.classesChanged.toArray(classDiffs); for (int k = 0; k < classDiffs.length; k++) { reportChangedClass(pkgName, classDiffs, k); } } writeSectionFooter(pkgName, prevPkgRef, nextPkgRef, null, 1); writeHTMLFooter(); reportFile.close(); reportFile = oldReportFile; } /** * Write out the details of a changed class in a separate file. */ public void reportChangedClass(String pkgName, ClassDiff[] classDiffs, int classIndex) { ClassDiff classDiff = classDiffs[classIndex]; String className = classDiff.name_; PrintWriter oldReportFile = null; oldReportFile = reportFile; String localReportFileName = null; try { localReportFileName = reportFileName + JDiff.DIR_SEP + pkgName + "." + className + reportFileExt; if (outputDir != null) localReportFileName = outputDir + JDiff.DIR_SEP + localReportFileName; FileOutputStream fos = new FileOutputStream(localReportFileName); reportFile = new PrintWriter(fos); writeStartHTMLHeader(); writeHTMLTitle(pkgName + "." + className); writeStyleSheetRef(); writeText(""); writeText(""); } catch(IOException e) { System.out.println("IO Error while attempting to create " + localReportFileName); System.out.println("Error: "+ e.getMessage()); System.exit(1); } String classRef = pkgName + "." + className; classRef = classRef.replace('.', '/'); if (className.indexOf('.') != -1) { classRef = pkgName + "."; classRef = classRef.replace('.', '/'); classRef = newDocPrefix + classRef + className; } else { classRef = newDocPrefix + classRef; } // A link to the class in the new API String linkedClassName = "" + className + ""; String lcn = pkgName + "." + linkedClassName; //Links to the previous and next classes String prevClassRef = null; if (classIndex != 0) { prevClassRef = pkgName + "." + classDiffs[classIndex-1].name_ + reportFileExt; } // Create the HTML link to the next package String nextClassRef = null; if (classIndex < classDiffs.length - 1) { nextClassRef = pkgName + "." + classDiffs[classIndex+1].name_ + reportFileExt; } if (classDiff.isInterface_) lcn = "Interface " + lcn; else lcn = "Class " + lcn; boolean hasCtors = classDiff.ctorsRemoved.size() != 0 || classDiff.ctorsAdded.size() != 0 || classDiff.ctorsChanged.size() != 0; boolean hasMethods = classDiff.methodsRemoved.size() != 0 || classDiff.methodsAdded.size() != 0 || classDiff.methodsChanged.size() != 0; boolean hasFields = classDiff.fieldsRemoved.size() != 0 || classDiff.fieldsAdded.size() != 0 || classDiff.fieldsChanged.size() != 0; writeSectionHeader(lcn, pkgName, prevClassRef, nextClassRef, className, 2, hasCtors, hasMethods, hasFields); if (classDiff.inheritanceChange_ != null) writeText("

" + classDiff.inheritanceChange_ + ""); // Report changes in documentation if (reportDocChanges && classDiff.documentationChange_ != null) { String oldClassRef = null; if (oldDocPrefix != null) { oldClassRef = pkgName + "." + className; oldClassRef = oldClassRef.replace('.', '/'); if (className.indexOf('.') != -1) { oldClassRef = pkgName + "."; oldClassRef = oldClassRef.replace('.', '/'); oldClassRef = oldDocPrefix + oldClassRef + className; } else { oldClassRef = oldDocPrefix + oldClassRef; } } if (oldDocPrefix != null) classDiff.documentationChange_ += "old to "; else classDiff.documentationChange_ += "old to "; classDiff.documentationChange_ += "new. "; writeText(classDiff.documentationChange_); } if (classDiff.modifiersChange_ != null) writeText("

" + classDiff.modifiersChange_); reportAllCtors(pkgName, classDiff); reportAllMethods(pkgName, classDiff); reportAllFields(pkgName, classDiff); writeSectionFooter(pkgName, prevClassRef, nextClassRef, className, 2); writeHTMLFooter(); reportFile.close(); reportFile = oldReportFile; } /** * Write out the details of constructors in a class. */ public void reportAllCtors(String pkgName, ClassDiff classDiff) { String className = classDiff.name_; writeText(""); // Named anchor // Report ctors which were removed in the new API if (classDiff.ctorsRemoved.size() != 0) { writeTableStart("Removed Constructors", 2); Iterator iter = classDiff.ctorsRemoved.iterator(); while (iter.hasNext()) { ConstructorAPI ctorAPI = (ConstructorAPI)(iter.next()); String ctorType = ctorAPI.getSignature(); if (ctorType.compareTo("void") == 0) ctorType = ""; String id = className + "(" + ctorType + ")"; if (trace) System.out.println("Constructor " + id + " was removed."); writeCtorTableEntry(pkgName, className, ctorType, 0, ctorAPI.doc_, false); } writeTableEnd(); } // Report ctors which were added in the new API if (classDiff.ctorsAdded.size() != 0) { writeTableStart("Added Constructors", 2); Iterator iter = classDiff.ctorsAdded.iterator(); while (iter.hasNext()) { ConstructorAPI ctorAPI = (ConstructorAPI)(iter.next()); String ctorType = ctorAPI.getSignature(); if (ctorType.compareTo("void") == 0) ctorType = ""; String id = className + "(" + ctorType + ")"; if (trace) System.out.println("Constructor " + id + " was added."); writeCtorTableEntry(pkgName, className, ctorType, 1, ctorAPI.doc_, false); } writeTableEnd(); } // Report ctors which were changed in the new API if (classDiff.ctorsChanged.size() != 0) { // Emit a table of changed classes, with links to the section // for each class. writeTableStart("Changed Constructors", 3); Iterator iter = classDiff.ctorsChanged.iterator(); while (iter.hasNext()) { MemberDiff memberDiff = (MemberDiff)(iter.next()); if (trace) System.out.println("Constructor for " + className + " was changed from " + memberDiff.oldType_ + " to " + memberDiff.newType_); writeCtorChangedTableEntry(pkgName, className, memberDiff); } writeTableEnd(); } } /** * Write out the details of methods in a class. */ public void reportAllMethods(String pkgName, ClassDiff classDiff) { writeText(""); // Named anchor String className = classDiff.name_; // Report methods which were removed in the new API if (classDiff.methodsRemoved.size() != 0) { writeTableStart("Removed Methods", 2); Iterator iter = classDiff.methodsRemoved.iterator(); while (iter.hasNext()) { MethodAPI methodAPI = (MethodAPI)(iter.next()); String methodName = methodAPI.name_ + "(" + methodAPI.getSignature() + ")"; if (trace) System.out.println("Method " + methodName + " was removed."); writeMethodTableEntry(pkgName, className, methodAPI, 0, methodAPI.doc_, false); } writeTableEnd(); } // Report methods which were added in the new API if (classDiff.methodsAdded.size() != 0) { writeTableStart("Added Methods", 2); Iterator iter = classDiff.methodsAdded.iterator(); while (iter.hasNext()) { MethodAPI methodAPI = (MethodAPI)(iter.next()); String methodName = methodAPI.name_ + "(" + methodAPI.getSignature() + ")"; if (trace) System.out.println("Method " + methodName + " was added."); writeMethodTableEntry(pkgName, className, methodAPI, 1, methodAPI.doc_, false); } writeTableEnd(); } // Report methods which were changed in the new API if (classDiff.methodsChanged.size() != 0) { // Emit a table of changed methods. writeTableStart("Changed Methods", 3); Iterator iter = classDiff.methodsChanged.iterator(); while (iter.hasNext()) { MemberDiff memberDiff = (MemberDiff)(iter.next()); if (trace) System.out.println("Method " + memberDiff.name_ + " was changed."); writeMethodChangedTableEntry(pkgName, className, memberDiff); } writeTableEnd(); } } /** * Write out the details of fields in a class. */ public void reportAllFields(String pkgName, ClassDiff classDiff) { writeText(""); // Named anchor String className = classDiff.name_; // Report fields which were removed in the new API if (classDiff.fieldsRemoved.size() != 0) { writeTableStart("Removed Fields", 2); Iterator iter = classDiff.fieldsRemoved.iterator(); while (iter.hasNext()) { FieldAPI fieldAPI = (FieldAPI)(iter.next()); String fieldName = fieldAPI.name_; if (trace) System.out.println("Field " + fieldName + " was removed."); writeFieldTableEntry(pkgName, className, fieldAPI, 0, fieldAPI.doc_, false); } writeTableEnd(); } // Report fields which were added in the new API if (classDiff.fieldsAdded.size() != 0) { writeTableStart("Added Fields", 2); Iterator iter = classDiff.fieldsAdded.iterator(); while (iter.hasNext()) { FieldAPI fieldAPI = (FieldAPI)(iter.next()); String fieldName = fieldAPI.name_; if (trace) System.out.println("Field " + fieldName + " was added."); writeFieldTableEntry(pkgName, className, fieldAPI, 1, fieldAPI.doc_, false); } writeTableEnd(); } // Report fields which were changed in the new API if (classDiff.fieldsChanged.size() != 0) { // Emit a table of changed classes, with links to the section // for each class. writeTableStart("Changed Fields", 3); Iterator iter = classDiff.fieldsChanged.iterator(); while (iter.hasNext()) { MemberDiff memberDiff = (MemberDiff)(iter.next()); if (trace) System.out.println("Field " + pkgName + "." + className + "." + memberDiff.name_ + " was changed from " + memberDiff.oldType_ + " to " + memberDiff.newType_); writeFieldChangedTableEntry(pkgName, className, memberDiff); } writeTableEnd(); } } /** * Write the start of the HTML header, together with the current * date and time in an HTML comment. */ public void writeStartHTMLHeaderWithDate() { writeStartHTMLHeader(true); } /** Write the start of the HTML header. */ public void writeStartHTMLHeader() { writeStartHTMLHeader(false); } /** Write the start of the HTML header. */ public void writeStartHTMLHeader(boolean addDate) { writeText(""); writeText(""); writeText(""); writeText(""); writeText(""); writeText(""); if (addDate) writeText(""); writeText(""); writeText(""); } /** Write the HTML title */ public void writeHTMLTitle(String title) { writeText(""); writeText(title); writeText(""); } /** * Write the HTML style sheet reference for files in the subdirectory. */ public void writeStyleSheetRef() { writeStyleSheetRef(false); } /** * Write the HTML style sheet reference. If inSameDir is set, don't add * "../" to the location. */ public void writeStyleSheetRef(boolean inSameDir) { if (inSameDir) { writeText(""); writeText(""); writeText(""); writeText(""); writeText(""); } else { writeText(""); writeText(""); writeText(""); writeText(""); writeText(""); } // This doesn't work in non-windows browsers, so have to change the stylesheet // writeText(""); // writeText(""); } /** Write the HTML footer. */ public void writeHTMLFooter() { writeText(""); writeText(""); writeText(""); writeText(""); } /** * Write a section header, which includes a navigation bar. * * @param title Title of the header. Contains any links necessary. * @param packageName The name of the current package, with no slashes or * links in it. May be null * @param prevElemLink An HTML link to the previous element (a package or * class). May be null. * @param nextElemLink An HTML link to the next element (a package or * class). May be null. * @param className The name of the current class, with no slashes or * links in it. May be null. * @param level 0 = overview, 1 = package, 2 = class/interface */ public void writeSectionHeader(String title, String packageName, String prevElemLink, String nextElemLink, String className, int level, boolean hasRemovals, boolean hasAdditions, boolean hasChanges) { writeNavigationBar(packageName, prevElemLink, nextElemLink, className, level, true, hasRemovals, hasAdditions, hasChanges); if (level != 0) { reportFile.println("

"); reportFile.println(title); reportFile.println("

"); } } /** * Write a section footer, which includes a navigation bar. * * @param packageName The name of the current package, with no slashes or * links in it. may be null * @param prevElemLink An HTML link to the previous element (a package or * class). May be null. * @param nextElemLink An HTML link to the next element (a package or * class). May be null. * @param className The name of the current class, with no slashes or * links in it. May be null * @param level 0 = overview, 1 = package, 2 = class/interface */ public void writeSectionFooter(String packageName, String prevElemLink, String nextElemLink, String className, int level) { writeText(""); writeText("
"); writeText(" "); writeText(" "); writeText("
"); writeText(" ©2008 Google - "); writeText(" Code Home - "); writeText(" Site Terms of Service - "); writeText(" Privacy Policy "); writeText(" "); writeText("
"); writeText(" Generated by JDiff
"); writeText(" "); writeText("
"); writeText("
"); writeText(""); /* reportFile.println("
"); writeNavigationBar(packageName, prevElemLink, nextElemLink, className, level, false, false, false, false); */ } /** * Write a navigation bar section header. * * @param pkgName The name of the current package, with no slashes or * links in it. * @param prevElemLink An HTML link to the previous element (a package or * class). May be null. * @param nextElemLink An HTML link to the next element (a package or * class). May be null. * @param className The name of the current class, with no slashes or * links in it. May be null. * @param level 0 = overview, 1 = package, 2 = class/interface */ public void writeNavigationBar(String pkgName, String prevElemLink, String nextElemLink, String className, int level, boolean upperNavigationBar, boolean hasRemovals, boolean hasAdditions, boolean hasChanges) { String oldAPIName = "Old API"; if (apiDiff.oldAPIName_ != null) oldAPIName = apiDiff.oldAPIName_; String newAPIName = "New API"; if (apiDiff.newAPIName_ != null) newAPIName = apiDiff.newAPIName_; SimpleDateFormat formatter = new SimpleDateFormat ("yyyy.MM.dd HH:mm"); Date day = new Date(); reportFile.println(""); reportFile.println("
"); reportFile.println(""); reportFile.println("
"); reportFile.println("
"); reportFile.println(" "); reportFile.println("
"); reportFile.println("
"); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); // reportFile.println(" "); // reportFile.println(" "); // reportFile.println(" "); // reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println("
API Diff Specification
To Version:" + newAPIName + "
From Version:" + oldAPIName + "
Product Type:Generic
Generated" + formatter.format( day ) + "
"); reportFile.println("
"); if (doStats) { reportFile.println("
"); reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); reportFile.println("
Statistics"); reportFile.println("
"); reportFile.println("
"); } reportFile.println("
"); reportFile.println("
"); /* reportFile.println(""); reportFile.println(" "); reportFile.println(" "); // The right hand side title, only added at the top if (upperNavigationBar) { reportFile.println(" "); } else { reportFile.println(" "); } reportFile.println(""); // Links for frames and no frames reportFile.println(""); // All of the previous and next links, and the frames and non-frames // links are in one table cell reportFile.println(" "); } else { reportFile.println("  NO FRAMES"); } } else { reportFile.println("  NO FRAMES"); } // All of the details links are in one table cell if (atClass) { // Links to a class page's sections // The meaning of these three variable is overloaded boolean hasCtors = hasRemovals; boolean hasMethods = hasAdditions; boolean hasFields = hasChanges; if (hasCtors || hasMethods || hasFields) { reportFile.println(" "); } else { // Make the end of the table line match the length of the top reportFile.println(""); } } else { // Links to a package page's sections if (hasRemovals || hasAdditions || hasChanges) { reportFile.println(" "); } else { // Make the end of the table line match the length of the top reportFile.println(""); } } reportFile.println(""); reportFile.println("
"); reportFile.println(" "); reportFile.println(" "); boolean atOverview = (level == 0); boolean atPackage = (level == 1); boolean atClass = (level == 2); // Always have a link to the Javadoc files if (atOverview) { reportFile.println(" "); } else if (atPackage) { String pkgRef = pkgName; pkgRef = pkgRef.replace('.', '/'); pkgRef = newDocPrefix + pkgRef + "/package-summary"; reportFile.println(" "); } else if (atClass) { String classRef = pkgName + "." + className; classRef = classRef.replace('.', '/'); if (className.indexOf('.') != -1) { classRef = pkgName + "."; classRef = classRef.replace('.', '/'); classRef = newDocPrefix + classRef + className; } else { classRef = newDocPrefix + classRef; } reportFile.println(" "); } if (atOverview) { reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); } String changesSummaryName = reportFileName + "-summary" + reportFileExt; if (atPackage) { reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); } if (atClass) { reportFile.println(" "); reportFile.println(" "); reportFile.println(" "); } if (!Diff.noDocDiffs) { if (atPackage) { String id = (String)Diff.firstDiffOutput.get(pkgName + "!package"); if (id != null) reportFile.println(" "); else reportFile.println(" "); } else if (atClass) { String id = (String)Diff.firstDiffOutput.get(pkgName + "." + className + "!class"); if (id != null) reportFile.println(" "); else reportFile.println(" "); } else { reportFile.println(" "); } } if (doStats) { reportFile.println(" "); } // Always have a link to the JDiff help file reportFile.println(" "); reportFile.println(" "); reportFile.println("
" + apiDiff.newAPIName_ + "  " + apiDiff.newAPIName_ + "  " + apiDiff.newAPIName_ + "   Overview   Package   Class  Overview   Package   Class  Overview  Package   Class  Text Changes  Text Changes  Text Changes  Text Changes  Text Changes  Statistics  Help 
"); reportFile.println("
Generated by
JDiff
"); // Display links to the previous and next packages or classes if (atPackage || atClass) { String elemName = "CLASS"; if (className == null) { elemName = "PACKAGE"; } if (prevElemLink == null) { reportFile.println(" PREV " + elemName + " "); } else { reportFile.println(" PREV " + elemName + ""); } if (nextElemLink == null) { reportFile.println(" NEXT " + elemName + " "); } else { reportFile.println(" NEXT " + elemName + ""); } reportFile.println("        "); } else { reportFile.println("   "); } // Links for frames and non-frames. reportFile.println(" FRAMES  "); if (className == null) { if (level == 0) { reportFile.println("  NO FRAMES DETAIL:  "); if (hasCtors) { reportFile.println("CONSTRUCTORS | "); } else { reportFile.println("CONSTRUCTORS | "); } if (hasMethods) { reportFile.println("METHODS | "); } else { reportFile.println("METHODS | "); } if (hasFields) { reportFile.println("FIELDS"); } else { reportFile.println("FIELDS"); } reportFile.println(" DETAIL:  "); if (hasRemovals) { reportFile.println("REMOVED | "); } else { reportFile.println("REMOVED | "); } if (hasAdditions) { reportFile.println("ADDED | "); } else { reportFile.println("ADDED | "); } if (hasChanges) { reportFile.println("CHANGED"); } else { reportFile.println("CHANGED"); } reportFile.println("
"); reportFile.println("
"); reportFile.println(""); */ } /** Write the start of a table. */ public void writeTableStart(String title, int colSpan) { reportFile.println("

"); // Assumes that the first word of the title categorizes the table type // and that there is a space after the first word in the title int idx = title.indexOf(' '); String namedAnchor = title.substring(0, idx); reportFile.println(""); // Named anchor reportFile.println(""); reportFile.println(""); reportFile.print(" "); } /** * If a class or package name is considered to be too long for convenient * display, insert
in the middle of it at a period. */ public String makeTwoRows(String name) { if (name.length() < 30) return name; int idx = name.indexOf(".", 20); if (idx == -1) return name; int len = name.length(); String res = name.substring(0, idx+1) + "
" + name.substring(idx+1, len); return res; } /** * Write a table entry for a package, with support for links to Javadoc * for removed packages. * * linkType: 0 - no link by default, 1 = link to Javadoc HTML file, 2 = link to JDiff file */ public void writePackageTableEntry(String pkgName, int linkType, String possibleComment, boolean useOld) { if (!useOld) { reportFile.println(""); reportFile.println(" "); emitComment(pkgName, possibleComment, linkType); reportFile.println(""); } } /** * Write a table entry for a class or interface. * * linkType: 0 - no link by default, 1 = link to Javadoc HTML file, 2 = link to JDiff file */ public void writeClassTableEntry(String pkgName, String className, int linkType, boolean isInterface, String possibleComment, boolean useOld) { if (!useOld) { reportFile.println(""); reportFile.println(" "); emitComment(fqName, possibleComment, linkType); reportFile.println(""); } } /** * Write a table entry for a constructor. * * linkType: 0 - no link by default, 1 = link to Javadoc HTML file */ public void writeCtorTableEntry(String pkgName, String className, String type, int linkType, String possibleComment, boolean useOld) { String fqName = pkgName + "." + className; String shownClassName = makeTwoRows(className); String lt = "removed"; if (linkType ==1) lt = "added"; String commentID = fqName + ".ctor_" + lt + "(" + type + ")"; if (!useOld) { reportFile.println(""); reportFile.println(" "); emitComment(commentID, possibleComment, linkType); reportFile.println(""); } } /** * Write a table entry for a changed constructor. */ public void writeCtorChangedTableEntry(String pkgName, String className, MemberDiff memberDiff) { String fqName = pkgName + "." + className; String newSignature = memberDiff.newType_; if (newSignature.compareTo("void") == 0) newSignature = ""; String commentID = fqName + ".ctor_changed(" + newSignature + ")"; reportFile.println(""); reportFile.println(" "); // Report changes in documentation if (reportDocChanges && memberDiff.documentationChange_ != null) { String oldMemberRef = null; String oldType = null; if (oldDocPrefix != null) { oldMemberRef = pkgName + "." + className; oldMemberRef = oldMemberRef.replace('.', '/'); if (className.indexOf('.') != -1) { oldMemberRef = pkgName + "."; oldMemberRef = oldMemberRef.replace('.', '/'); oldMemberRef = oldDocPrefix + oldMemberRef + className; } else { oldMemberRef = oldDocPrefix + oldMemberRef; } oldType = memberDiff.oldType_; if (oldType.compareTo("void") == 0) oldType = ""; } if (oldDocPrefix != null) memberDiff.documentationChange_ += "old to "; else memberDiff.documentationChange_ += "old to "; memberDiff.documentationChange_ += "new.
"; } emitChanges(memberDiff, 0); emitComment(commentID, null, 2); reportFile.println(""); } /** * Write a table entry for a method. * * linkType: 0 - no link by default, 1 = link to Javadoc HTML file */ public void writeMethodTableEntry(String pkgName, String className, MethodAPI methodAPI, int linkType, String possibleComment, boolean useOld) { String fqName = pkgName + "." + className; String signature = methodAPI.getSignature(); String methodName = methodAPI.name_; String lt = "removed"; if (linkType ==1) lt = "added"; String commentID = fqName + "." + methodName + "_" + lt + "(" + signature + ")"; if (!useOld) { reportFile.println(""); reportFile.println(" "); emitComment(commentID, possibleComment, linkType); reportFile.println(""); } } /** * Write a table entry for a changed method. */ public void writeMethodChangedTableEntry(String pkgName, String className, MemberDiff memberDiff) { String memberName = memberDiff.name_; // Generally nowhere to break a member name anyway // String shownMemberName = makeTwoRows(memberName); String fqName = pkgName + "." + className; String newSignature = memberDiff.newSignature_; String commentID = fqName + "." + memberName + "_changed(" + newSignature + ")"; reportFile.println(""); reportFile.println(" "); // Report changes in documentation if (reportDocChanges && memberDiff.documentationChange_ != null) { String oldMemberRef = null; String oldSignature = null; if (oldDocPrefix != null) { oldMemberRef = pkgName + "." + className; oldMemberRef = oldMemberRef.replace('.', '/'); if (className.indexOf('.') != -1) { oldMemberRef = pkgName + "."; oldMemberRef = oldMemberRef.replace('.', '/'); oldMemberRef = oldDocPrefix + oldMemberRef + className; } else { oldMemberRef = oldDocPrefix + oldMemberRef; } oldSignature = memberDiff.oldSignature_; } if (oldDocPrefix != null) memberDiff.documentationChange_ += "old to "; else memberDiff.documentationChange_ += "old to "; memberDiff.documentationChange_ += "new.
"; } emitChanges(memberDiff, 1); // Get the comment from the parent class if more appropriate if (memberDiff.modifiersChange_ != null) { int parentIdx = memberDiff.modifiersChange_.indexOf("now inherited from"); if (parentIdx != -1) { // Change the commentID to pick up the appropriate method commentID = memberDiff.inheritedFrom_ + "." + memberName + "_changed(" + newSignature + ")"; } } emitComment(commentID, null, 2); reportFile.println(""); } /** * Write a table entry for a field. * * linkType: 0 - no link by default, 1 = link to Javadoc HTML file */ public void writeFieldTableEntry(String pkgName, String className, FieldAPI fieldAPI, int linkType, String possibleComment, boolean useOld) { String fqName = pkgName + "." + className; // Fields can only appear in one table, so no need to specify _added etc String fieldName = fieldAPI.name_; // Generally nowhere to break a member name anyway // String shownFieldName = makeTwoRows(fieldName); String commentID = fqName + "." + fieldName; if (!useOld) { reportFile.println(""); reportFile.println(" "); emitComment(commentID, possibleComment, linkType); reportFile.println(""); } } /** * Write a table entry for a changed field. */ public void writeFieldChangedTableEntry(String pkgName, String className, MemberDiff memberDiff) { String memberName = memberDiff.name_; // Generally nowhere to break a member name anyway // String shownMemberName = makeTwoRows(memberName); String fqName = pkgName + "." + className; // Fields have unique names in a class String commentID = fqName + "." + memberName; reportFile.println(""); reportFile.println(" "); // Report changes in documentation if (reportDocChanges && memberDiff.documentationChange_ != null) { String oldMemberRef = null; if (oldDocPrefix != null) { oldMemberRef = pkgName + "." + className; oldMemberRef = oldMemberRef.replace('.', '/'); if (className.indexOf('.') != -1) { oldMemberRef = pkgName + "."; oldMemberRef = oldMemberRef.replace('.', '/'); oldMemberRef = oldDocPrefix + oldMemberRef + className; } else { oldMemberRef = oldDocPrefix + oldMemberRef; } } if (oldDocPrefix != null) memberDiff.documentationChange_ += "old to "; else memberDiff.documentationChange_ += "old to "; memberDiff.documentationChange_ += "new.
"; } emitChanges(memberDiff, 2); // Get the comment from the parent class if more appropriate if (memberDiff.modifiersChange_ != null) { int parentIdx = memberDiff.modifiersChange_.indexOf("now inherited from"); if (parentIdx != -1) { // Change the commentID to pick up the appropriate method commentID = memberDiff.inheritedFrom_ + "." + memberName; } } emitComment(commentID, null, 2); reportFile.println(""); } /** * Emit all changes associated with a MemberDiff as an entry in a table. * * @param memberType 0 = ctor, 1 = method, 2 = field */ public void emitChanges(MemberDiff memberDiff, int memberType){ reportFile.println(" "); } /** * Emit a string which is an exception by surrounding it with * <code> tags. * If there is a space in the type, e.g. "String, File", then * surround it with parentheses too. Do not add <code> tags or * parentheses if the String is "no exceptions". */ public void emitException(String ex) { if (ex.compareTo("no exceptions") == 0) { reportFile.print(ex); } else { if (ex.indexOf(' ') != -1) { reportFile.print("(" + ex + ")"); } else { reportFile.print("" + ex + ""); } } } /** * Emit a string which is a type by surrounding it with <code> tags. * If there is a space in the type, e.g. "String, File", then * surround it with parentheses too. */ public void emitType(String type) { if (type.compareTo("") == 0) return; if (type.indexOf(' ') != -1) { reportFile.print("(" + type + ")"); } else { reportFile.print("" + type + ""); } } /** * Emit a string which is a type by surrounding it with <code> tags. * Also surround it with parentheses too. Used to display methods' * parameters. * Suggestions for where a browser should break the * text are provided with <br> and <nobr> tags. */ public static void emitTypeWithParens(String type) { emitTypeWithParens(type, true); } /** * Emit a string which is a type by surrounding it with <code> tags. * Also surround it with parentheses too. Used to display methods' * parameters. */ public static void emitTypeWithParens(String type, boolean addBreaks) { if (type.compareTo("") == 0) reportFile.print("()"); else { int idx = type.indexOf(", "); if (!addBreaks || idx == -1) { reportFile.print("(" + type + ")"); } else { // Make the browser break text at reasonable places String sepType = null; StringTokenizer st = new StringTokenizer(type, ", "); while (st.hasMoreTokens()) { String p = st.nextToken(); if (sepType == null) sepType = p; else sepType += ", " + p + ""; } reportFile.print("(" + sepType + ")"); } } } /** * Emit a string which is a type by surrounding it with <code> tags. * Do not surround it with parentheses. Used to display methods' return * types and field types. */ public static void emitTypeWithNoParens(String type) { if (type.compareTo("") != 0) reportFile.print("" + type + ""); } /** * Return a String with the simple names of the classes in fqName. * "java.lang.String" becomes "String", * "java.lang.String, java.io.File" becomes "String, File" * and so on. If fqName is null, return null. If fqName is "", * return "". */ public static String simpleName(String fqNames) { if (fqNames == null) return null; String res = ""; boolean hasContent = false; // We parse the string step by step to ensure we take // fqNames that contains generics parameter in a whole. ArrayList fqNamesList = new ArrayList(); int genericParametersDepth = 0; StringBuffer buffer = new StringBuffer(); for (int i=0; i' == c) { genericParametersDepth--; } if (',' != c || genericParametersDepth > 0) { buffer.append(c); } else if (',' == c) { fqNamesList.add(buffer.toString().trim()); buffer = new StringBuffer(buffer.length()); } } fqNamesList.add(buffer.toString().trim()); for (String fqName : fqNamesList) { // Assume this will be used inside a set of tags. if (hasContent) res += ", "; hasContent = true; // Look for text within '<' and '>' in case this is a invocation of a generic int firstBracket = fqName.indexOf('<'); int lastBracket = fqName.lastIndexOf('>'); String genericParameter = null; if (firstBracket != -1 && lastBracket != -1) { genericParameter = simpleName(fqName.substring(firstBracket + 1, lastBracket)); fqName = fqName.substring(0, firstBracket); } int lastDot = fqName.lastIndexOf('.'); if (lastDot < 0) { res += fqName; // Already as simple as possible } else { res += fqName.substring(lastDot+1); } if (genericParameter != null) res += "<" + genericParameter + ">"; } return res; } /** * Find any existing comment and emit it. Add the new comment to the * list of new comments. The first instance of the string "@first" in * a hand-written comment will be replaced by the first sentence from * the associated doc block, if such exists. Also replace @link by * an HTML link. * * @param commentID The identifier for this comment. * @param possibleComment A possible comment from another source. * @param linkType 0 = remove, 1 = add, 2 = change */ public void emitComment(String commentID, String possibleComment, int linkType) { if (noCommentsOnRemovals && linkType == 0) { reportFile.println(" "); return; } if (noCommentsOnAdditions && linkType == 1) { reportFile.println(" "); return; } if (noCommentsOnChanges && linkType == 2) { reportFile.println(" "); return; } // We have to use this global hash table because the *Diff classes // do not store the possible comment from the new *API object. if (!noCommentsOnChanges && possibleComment == null) { possibleComment = (String)Comments.allPossibleComments.get(commentID); } // Just use the first sentence of the possible comment. if (possibleComment != null) { int fsidx = RootDocToXML.endOfFirstSentence(possibleComment, false); if (fsidx != -1 && fsidx != 0) possibleComment = possibleComment.substring(0, fsidx+1); } String comment = Comments.getComment(existingComments_, commentID); if (comment.compareTo(Comments.placeHolderText) == 0) { if (possibleComment != null && possibleComment.indexOf("InsertOtherCommentsHere") == -1) reportFile.println(" "); else reportFile.println(" "); } else { int idx = comment.indexOf("@first"); if (idx == -1) { reportFile.println(" "); } else { reportFile.print(" "); } } SingleComment newComment = new SingleComment(commentID, comment); newComments_.addComment(newComment); } /** Write the end of a table. */ public void writeTableEnd() { reportFile.println("
"); reportFile.println(title + ""); reportFile.println("
"); reportFile.println(" "); // Named anchor } //String shownPkgName = makeTwoRows(pkgName); if (linkType == 0) { if (oldDocPrefix == null) { // No link reportFile.print(" " + pkgName); } else { // Call this method again but this time to emit a link to the // old program element. writePackageTableEntry(pkgName, 1, possibleComment, true); } } else if (linkType == 1) { // Link to HTML file for the package String pkgRef = pkgName; pkgRef = pkgRef.replace('.', '/'); if (useOld) pkgRef = oldDocPrefix + pkgRef + "/package-summary"; else pkgRef = newDocPrefix + pkgRef + "/package-summary"; reportFile.println(" " + pkgName + ""); } else if (linkType == 2) { reportFile.println(" " + pkgName + ""); } if (!useOld) { reportFile.println("
"); reportFile.println(" "); // Named anchor } String fqName = pkgName + "." + className; String shownClassName = makeTwoRows(className); if (linkType == 0) { if (oldDocPrefix == null) { // No link if (isInterface) reportFile.println(" " + shownClassName + ""); else reportFile.println(" " + shownClassName); } else { writeClassTableEntry(pkgName, className, 1, isInterface, possibleComment, true); } } else if (linkType == 1) { // Link to HTML file for the class String classRef = fqName; // Deal with inner classes if (className.indexOf('.') != -1) { classRef = pkgName + "."; classRef = classRef.replace('.', '/'); if (useOld) classRef = oldDocPrefix + classRef + className; else classRef = newDocPrefix + classRef + className; } else { classRef = classRef.replace('.', '/'); if (useOld) classRef = oldDocPrefix + classRef; else classRef = newDocPrefix + classRef; } reportFile.print(" "); if (isInterface) reportFile.print("" + shownClassName + ""); else reportFile.print(shownClassName); reportFile.println(""); } else if (linkType == 2) { reportFile.print(" "); if (isInterface) reportFile.print("" + shownClassName + ""); else reportFile.print(shownClassName); reportFile.println(""); } if (!useOld) { reportFile.println("
"); reportFile.println(" "); // Named anchor } String shortType = simpleName(type); if (linkType == 0) { if (oldDocPrefix == null) { // No link reportFile.print(" " + pkgName); emitTypeWithParens(shortType); reportFile.println(""); } else { writeCtorTableEntry(pkgName, className, type, 1, possibleComment, true); } } else if (linkType == 1) { // Link to HTML file for the package String memberRef = fqName.replace('.', '/'); // Deal with inner classes if (className.indexOf('.') != -1) { memberRef = pkgName + "."; memberRef = memberRef.replace('.', '/'); if (useOld) { // oldDocPrefix is non-null at this point memberRef = oldDocPrefix + memberRef + className; } else { memberRef = newDocPrefix + memberRef + className; } } else { if (useOld) { // oldDocPrefix is non-null at this point memberRef = oldDocPrefix + memberRef; } else { memberRef = newDocPrefix + memberRef; } } reportFile.print(" " + shownClassName + ""); emitTypeWithParens(shortType); reportFile.println(""); } if (!useOld) { reportFile.println("
"); reportFile.println(" "); // Named anchor String memberRef = fqName.replace('.', '/'); String shownClassName = makeTwoRows(className); // Deal with inner classes if (className.indexOf('.') != -1) { memberRef = pkgName + "."; memberRef = memberRef.replace('.', '/'); memberRef = newDocPrefix + memberRef + className; } else { memberRef = newDocPrefix + memberRef; } String newType = memberDiff.newType_; if (newType.compareTo("void") == 0) newType = ""; String shortNewType = simpleName(memberDiff.newType_); // Constructors have the linked name, then the type in parentheses. reportFile.print(" "); reportFile.print(shownClassName); reportFile.print(""); emitTypeWithParens(shortNewType); reportFile.println(" "); reportFile.println("
"); reportFile.println(" "); // Named anchor } if (signature.compareTo("void") == 0) signature = ""; String shortSignature = simpleName(signature); String returnType = methodAPI.returnType_; String shortReturnType = simpleName(returnType); if (linkType == 0) { if (oldDocPrefix == null) { // No link reportFile.print(" "); emitType(shortReturnType); reportFile.print(" " + methodName); emitTypeWithParens(shortSignature); reportFile.println(""); } else { writeMethodTableEntry(pkgName, className, methodAPI, 1, possibleComment, true); } } else if (linkType == 1) { // Link to HTML file for the package String memberRef = fqName.replace('.', '/'); // Deal with inner classes if (className.indexOf('.') != -1) { memberRef = pkgName + "."; memberRef = memberRef.replace('.', '/'); if (useOld) { // oldDocPrefix is non-null at this point memberRef = oldDocPrefix + memberRef + className; } else { memberRef = newDocPrefix + memberRef + className; } } else { if (useOld) { // oldDocPrefix is non-null at this point memberRef = oldDocPrefix + memberRef; } else { memberRef = newDocPrefix + memberRef; } } reportFile.print(" "); emitType(shortReturnType); reportFile.print(" " + methodName + ""); emitTypeWithParens(shortSignature); reportFile.println(""); } if (!useOld) { reportFile.println("
"); reportFile.println(" "); // Named anchor String memberRef = fqName.replace('.', '/'); // Deal with inner classes if (className.indexOf('.') != -1) { memberRef = pkgName + "."; memberRef = memberRef.replace('.', '/'); memberRef = newDocPrefix + memberRef + className; } else { memberRef = newDocPrefix + memberRef; } // Javadoc generated HTML has no named anchors for methods // inherited from other classes, so link to the defining class' method. // Only copes with non-inner classes. if (className.indexOf('.') == -1 && memberDiff.modifiersChange_ != null && memberDiff.modifiersChange_.indexOf("but is now inherited from") != -1) { memberRef = memberDiff.inheritedFrom_; memberRef = memberRef.replace('.', '/'); memberRef = newDocPrefix + memberRef; } String newReturnType = memberDiff.newType_; String shortReturnType = simpleName(newReturnType); String shortSignature = simpleName(newSignature); reportFile.print(" "); emitTypeWithNoParens(shortReturnType); reportFile.print(" "); reportFile.print(memberName); reportFile.print(""); emitTypeWithParens(shortSignature); reportFile.println(" "); reportFile.println("
"); reportFile.println(" "); // Named anchor } String fieldType = fieldAPI.type_; if (fieldType.compareTo("void") == 0) fieldType = ""; String shortFieldType = simpleName(fieldType); if (linkType == 0) { if (oldDocPrefix == null) { // No link. reportFile.print(" "); emitType(shortFieldType); reportFile.println(" " + fieldName); } else { writeFieldTableEntry(pkgName, className, fieldAPI, 1, possibleComment, true); } } else if (linkType == 1) { // Link to HTML file for the package. String memberRef = fqName.replace('.', '/'); // Deal with inner classes if (className.indexOf('.') != -1) { memberRef = pkgName + "."; memberRef = memberRef.replace('.', '/'); if (useOld) memberRef = oldDocPrefix + memberRef + className; else memberRef = newDocPrefix + memberRef + className; } else { if (useOld) memberRef = oldDocPrefix + memberRef; else memberRef = newDocPrefix + memberRef; } reportFile.print(" "); emitType(shortFieldType); reportFile.println(" " + fieldName + ""); } if (!useOld) { reportFile.println("
"); reportFile.println(" "); // Named anchor String memberRef = fqName.replace('.', '/'); // Deal with inner classes if (className.indexOf('.') != -1) { memberRef = pkgName + "."; memberRef = memberRef.replace('.', '/'); memberRef = newDocPrefix + memberRef + className; } else { memberRef = newDocPrefix + memberRef; } // Javadoc generated HTML has no named anchors for fields // inherited from other classes, so link to the defining class' field. // Only copes with non-inner classes. if (className.indexOf('.') == -1 && memberDiff.modifiersChange_ != null && memberDiff.modifiersChange_.indexOf("but is now inherited from") != -1) { memberRef = memberDiff.inheritedFrom_; memberRef = memberRef.replace('.', '/'); memberRef = newDocPrefix + memberRef; } String newType = memberDiff.newType_; String shortNewType = simpleName(newType); reportFile.print(" "); emitTypeWithNoParens(shortNewType); reportFile.print(" "); reportFile.print(memberName); reportFile.print(""); reportFile.println("
"); boolean hasContent = false; // The type or return type changed if (memberDiff.oldType_.compareTo(memberDiff.newType_) != 0) { String shortOldType = simpleName(memberDiff.oldType_); String shortNewType = simpleName(memberDiff.newType_); if (memberType == 1) { reportFile.print("Change in return type from "); } else { reportFile.print("Change in type from "); } if (shortOldType.compareTo(shortNewType) == 0) { // The types differ in package name, so use the full name shortOldType = memberDiff.oldType_; shortNewType = memberDiff.newType_; } emitType(shortOldType); reportFile.print(" to "); emitType(shortNewType); reportFile.println(".
"); hasContent = true; } // The signatures changed - only used by methods if (memberType == 1 && memberDiff.oldSignature_ != null && memberDiff.newSignature_ != null && memberDiff.oldSignature_.compareTo(memberDiff.newSignature_) != 0) { String shortOldSignature = simpleName(memberDiff.oldSignature_); String shortNewSignature = simpleName(memberDiff.newSignature_); if (shortOldSignature.compareTo(shortNewSignature) == 0) { // The signatures differ in package names, so use the full form shortOldSignature = memberDiff.oldSignature_; shortNewSignature = memberDiff.newSignature_; } if (hasContent) reportFile.print(" "); reportFile.print("Change in signature from "); if (shortOldSignature.compareTo("") == 0) shortOldSignature = "void"; emitType(shortOldSignature); reportFile.print(" to "); if (shortNewSignature.compareTo("") == 0) shortNewSignature = "void"; emitType(shortNewSignature); reportFile.println(".
"); hasContent = true; } // The exceptions are only non-null in methods and constructors if (memberType != 2 && memberDiff.oldExceptions_ != null && memberDiff.newExceptions_ != null && memberDiff.oldExceptions_.compareTo(memberDiff.newExceptions_) != 0) { if (hasContent) reportFile.print(" "); // If either one of the exceptions has no spaces in it, or is // equal to "no exceptions", then just display the whole // exceptions texts. int spaceInOld = memberDiff.oldExceptions_.indexOf(" "); if (memberDiff.oldExceptions_.compareTo("no exceptions") == 0) spaceInOld = -1; int spaceInNew = memberDiff.newExceptions_.indexOf(" "); if (memberDiff.newExceptions_.compareTo("no exceptions") == 0) spaceInNew = -1; if (spaceInOld == -1 || spaceInNew == -1) { reportFile.print("Change in exceptions thrown from "); emitException(memberDiff.oldExceptions_); reportFile.print(" to " ); emitException(memberDiff.newExceptions_); reportFile.println(".
"); } else { // Too many exceptions become unreadable, so just show the // individual changes. Catch the case where exceptions are // just reordered. boolean firstChange = true; int numRemoved = 0; StringTokenizer stOld = new StringTokenizer(memberDiff.oldExceptions_, ", "); while (stOld.hasMoreTokens()) { String oldException = stOld.nextToken(); if (!memberDiff.newExceptions_.startsWith(oldException) && !(memberDiff.newExceptions_.indexOf(", " + oldException) != -1)) { if (firstChange) { reportFile.print("Change in exceptions: "); firstChange = false; } if (numRemoved != 0) reportFile.print(", "); emitException(oldException); numRemoved++; } } if (numRemoved == 1) reportFile.print(" was removed."); else if (numRemoved > 1) reportFile.print(" were removed."); int numAdded = 0; StringTokenizer stNew = new StringTokenizer(memberDiff.newExceptions_, ", "); while (stNew.hasMoreTokens()) { String newException = stNew.nextToken(); if (!memberDiff.oldExceptions_.startsWith(newException) && !(memberDiff.oldExceptions_.indexOf(", " + newException) != -1)) { if (firstChange) { reportFile.print("Change in exceptions: "); firstChange = false; } if (numAdded != 0) reportFile.println(", "); else reportFile.println(" "); emitException(newException); numAdded++; } } if (numAdded == 1) reportFile.print(" was added"); else if (numAdded > 1) reportFile.print(" were added"); else if (numAdded == 0 && numRemoved == 0 && firstChange) reportFile.print("Exceptions were reordered"); reportFile.println(".
"); } // Note the changes between a comma-separated list of Strings hasContent = true; } if (memberDiff.documentationChange_ != null) { if (hasContent) reportFile.print(" "); reportFile.print(memberDiff.documentationChange_); hasContent = true; } // Last, so no need for a
if (memberDiff.modifiersChange_ != null) { if (hasContent) reportFile.print(" "); reportFile.println(memberDiff.modifiersChange_); hasContent = true; } reportFile.println("
   " + possibleComment + " " + Comments.convertAtLinks(comment, "", null, null) + "" + comment.substring(0, idx)); if (possibleComment != null && possibleComment.indexOf("InsertOtherCommentsHere") == -1) reportFile.print(possibleComment); reportFile.println(comment.substring(idx + 6) + "
"); reportFile.println(" "); } /** Write a newline out. */ public void writeText() { reportFile.println(); } /** Write some text out. */ public void writeText(String text) { reportFile.println(text); } /** Emit some non-breaking space for indentation. */ public void indent(int indent) { for (int i = 0; i < indent; i++) reportFile.print(" "); } /** * The name of the file to which the top-level HTML file is written, * and also the name of the subdirectory where most of the HTML appears, * and also a prefix for the names of some of the files in that * subdirectory. */ static String reportFileName = "changes"; /** * The suffix of the file to which the HTML output is currently being * written. */ static String reportFileExt = ".html"; /** * The file to which the HTML output is currently being written. */ static PrintWriter reportFile = null; /** * The object which represents the top of the tree of differences * between two APIs. It is only used indirectly when emitting a * navigation bar. */ static APIDiff apiDiff = null; /** * If set, then do not suggest comments for removals from the first * sentence of the doc block of the old API. */ public static boolean noCommentsOnRemovals = false; /** * If set, then do not suggest comments for additions from the first * sentence of the doc block of the new API. */ public static boolean noCommentsOnAdditions = false; /** * If set, then do not suggest comments for changes from the first * sentence of the doc block of the new API. */ public static boolean noCommentsOnChanges = false; /** * If set, then report changes in documentation (Javadoc comments) * between the old and the new API. The default is that this is not set. */ public static boolean reportDocChanges = false; /** * Define the prefix for HTML links to the existing set of Javadoc- * generated documentation for the new API. E.g. For J2SE1.3.x, use * "http://java.sun.com/j2se/1.3/docs/api/" */ public static String newDocPrefix = "../"; /** * Define the prefix for HTML links to the existing set of Javadoc- * generated documentation for the old API. */ public static String oldDocPrefix = null; /** To generate statistical output, set this to true. */ public static boolean doStats = false; /** * The destination directory for output files. */ public static String outputDir = null; /** * The destination directory for comments files (if not specified, uses outputDir) */ public static String commentsDir = null; /** * The title used on the first page of the report. By default, this is * "API Differences Between <name of old API> and * <name of new API>". It can be * set by using the -doctitle option. */ public static String docTitle = null; /** * The browser window title for the report. By default, this is * "API Differences Between <name of old API> and * <name of new API>". It can be * set by using the -windowtitle option. */ public static String windowTitle = null; /** The desired background color for JDiff tables. */ static final String bgcolor = "#FFFFFF"; /** Set to enable debugging output. */ private static final boolean trace = false; }