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