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