1 /** 2 ******************************************************************************* 3 * Copyright (C) 2004-2010, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8 /** 9 * Generate a list of ICU's public APIs, sorted by qualified name and signature 10 * public APIs are all non-internal, non-package apis in com.ibm.icu.[lang|math|text|util]. 11 * For each API, list 12 * - public, package, protected, or private (PB PK PT PR) 13 * - static or non-static (STK NST) 14 * - final or non-final (FN NF) 15 * - synchronized or non-synchronized (SYN NSY) 16 * - stable, draft, deprecated, obsolete (ST DR DP OB) 17 * - abstract or non-abstract (AB NA) 18 * - constructor, member, field (C M F) 19 * 20 * Requires JDK 1.4.2 or later 21 * 22 * Sample invocation: 23 * c:/j2sdk1.4.2/bin/javadoc 24 * -classpath c:/jd2sk1.4.2/lib/tools.jar 25 * -doclet com.ibm.icu.dev.tool.docs.CheckAPI 26 * -docletpath c:/doug/cvsproj/icu4j/src 27 * -sourcepath c:/eclipse2.1/workspace2/icu4j/src 28 * -compare c:/doug/cvsproj/icu4j/src/com/ibm/icu/dev/tool/docs/api2_6_1.txt 29 * -output foo 30 * com.ibm.icu.text 31 * 32 * todo: separate generation of data files (which requires taglet) from 33 * comparison and report generation (which does not require it) 34 * todo: provide command-line control of filters of which subclasses/packages to process 35 * todo: record full inheritance heirarchy, not just immediate inheritance 36 * todo: allow for aliasing comparisons (force (pkg.)*class to be treated as though it 37 * were in a different pkg/class heirarchy (facilitates comparison of icu4j and java) 38 */ 39 40 package com.ibm.icu.dev.tool.docs; 41 42 import java.io.BufferedReader; 43 import java.io.BufferedWriter; 44 import java.io.File; 45 import java.io.FileInputStream; 46 import java.io.FileNotFoundException; 47 import java.io.FileOutputStream; 48 import java.io.IOException; 49 import java.io.InputStream; 50 import java.io.InputStreamReader; 51 import java.io.OutputStream; 52 import java.io.OutputStreamWriter; 53 import java.util.ArrayList; 54 import java.util.Collection; 55 import java.util.Comparator; 56 import java.util.Iterator; 57 import java.util.TreeSet; 58 59 import com.sun.javadoc.ClassDoc; 60 import com.sun.javadoc.ConstructorDoc; 61 import com.sun.javadoc.Doc; 62 import com.sun.javadoc.ExecutableMemberDoc; 63 import com.sun.javadoc.FieldDoc; 64 import com.sun.javadoc.MethodDoc; 65 import com.sun.javadoc.ProgramElementDoc; 66 import com.sun.javadoc.RootDoc; 67 import com.sun.javadoc.Tag; 68 69 public class CheckAPI { 70 RootDoc root; 71 String compare; // file 72 String compareName; 73 TreeSet compareSet; 74 TreeSet results; 75 boolean html; 76 String srcName = "Current"; // default source name 77 String output; 78 79 private static final int DATA_FILE_VERSION = 1; 80 private static final char SEP = ';'; 81 82 private static final int STA = 0, STA_DRAFT = 0, STA_STABLE = 1, STA_DEPRECATED = 2, STA_OBSOLETE = 3; 83 private static final int VIS = 1, VIS_PACKAGE = 0, VIS_PUBLIC= 1, VIS_PROTECTED = 2, VIS_PRIVATE = 3; 84 private static final int STK = 2, STK_STATIC = 1; 85 private static final int FIN = 3, FIN_FINAL = 1; 86 private static final int SYN = 4, SYN_SYNCHRONIZED = 1; 87 private static final int ABS = 5, ABS_ABSTRACT = 1; 88 private static final int CAT = 6, CAT_CLASS = 0, CAT_FIELD = 1, CAT_CONSTRUCTOR = 2, CAT_METHOD = 3; 89 private static final int PAK = 7; 90 private static final int CLS = 8; 91 private static final int NAM = 9; 92 private static final int SIG = 10; 93 private static final int EXC = 11; 94 private static final int NUM_TYPES = 11; 95 96 static abstract class APIInfo { 97 public abstract int getVal(int typ); 98 public abstract String get(int typ, boolean brief); 99 public abstract void write(BufferedWriter w, boolean brief, boolean html, boolean detail); 100 } 101 102 final static class Info extends APIInfo { 103 private int info; 104 private String pack; // package 105 private String cls; // enclosing class 106 private String name; // name 107 private String sig; // signature, class: inheritance, method: signature, field: type, const: signature 108 private String exc; // throws 109 110 public int getVal(int typ) { 111 validateType(typ); 112 return (info >> (typ*2)) & 0x3; 113 } 114 115 public String get(int typ, boolean brief) { 116 validateType(typ); 117 String[] vals = brief ? shortNames[typ] : names[typ]; 118 if (vals == null) { 119 switch (typ) { 120 case PAK: return pack; 121 case CLS: return cls; 122 case NAM: return name; 123 case SIG: return sig; 124 case EXC: return exc; 125 } 126 } 127 int val = (info >> (typ*2)) & 0x3; 128 return vals[val]; 129 } 130 131 private void setType(int typ, int val) { 132 validateType(typ); 133 info &= ~(0x3 << (typ*2)); 134 info |= (val&0x3) << (typ * 2); 135 } 136 137 private void setType(int typ, String val) { 138 validateType(typ); 139 String[] vals = shortNames[typ]; 140 if (vals == null) { 141 switch (typ) { 142 case PAK: pack = val; break; 143 case CLS: cls = val; break; 144 case NAM: name = val; break; 145 case SIG: sig = val; break; 146 case EXC: exc = val; break; 147 } 148 return; 149 } 150 151 for (int i = 0; i < vals.length; ++i) { 152 if (val.equalsIgnoreCase(vals[i])) { 153 info &= ~(0x3 << (typ*2)); 154 info |= i << (typ*2); 155 return; 156 } 157 } 158 159 throw new IllegalArgumentException("unrecognized value '" + val + "' for type '" + typeNames[typ] + "'"); 160 } 161 162 public void write(BufferedWriter w, boolean brief, boolean html, boolean detail) { 163 try { 164 if (brief) { 165 for (int i = 0; i < NUM_TYPES; ++i) { 166 String s = get(i, true); 167 if (s != null) { 168 w.write(s); 169 } 170 w.write(SEP); 171 } 172 } else { 173 // remove all occurrences of icu packages from the param string 174 // fortunately, all the packages have 4 chars (lang, math, text, util). 175 String xsig = sig; 176 if (!detail) { 177 final String ICUPACK = "com.ibm.icu."; 178 StringBuffer buf = new StringBuffer(); 179 for (int i = 0; i < sig.length();) { 180 int n = sig.indexOf(ICUPACK, i); 181 if (n == -1) { 182 buf.append(sig.substring(i)); 183 break; 184 } 185 buf.append(sig.substring(i, n)); 186 i = n + ICUPACK.length() + 5; // trailing 'xxxx.' 187 } 188 xsig = buf.toString(); 189 } 190 191 // construct signature 192 for (int i = STA; i < CAT; ++i) { // include status 193 String s = get(i, false); 194 if (s != null && s.length() > 0) { 195 if (i == STA) { 196 w.write('('); 197 w.write(s); 198 w.write(')'); 199 } else { 200 w.write(s); 201 } 202 w.write(' '); 203 } 204 } 205 206 int val = getVal(CAT); 207 switch (val) { 208 case CAT_CLASS: 209 if (sig.indexOf("extends") == -1) { 210 w.write("interface "); 211 } else { 212 w.write("class "); 213 } 214 if (cls.length() > 0) { 215 w.write(cls); 216 w.write('.'); 217 } 218 w.write(name); 219 if (detail) { 220 w.write(' '); 221 w.write(sig); 222 } 223 break; 224 225 case CAT_FIELD: 226 w.write(xsig); 227 w.write(' '); 228 w.write(name); 229 break; 230 231 case CAT_METHOD: 232 case CAT_CONSTRUCTOR: 233 int n = xsig.indexOf('('); 234 if (n > 0) { 235 w.write(xsig.substring(0, n)); 236 w.write(' '); 237 } else { 238 n = 0; 239 } 240 w.write(name); 241 w.write(xsig.substring(n)); 242 break; 243 } 244 } 245 w.newLine(); 246 } 247 catch (IOException e) { 248 RuntimeException re = new RuntimeException("IO Error"); 249 re.initCause(e); 250 throw re; 251 } 252 } 253 254 public boolean read(BufferedReader r) { 255 int i = 0; 256 try { 257 for (; i < NUM_TYPES; ++i) { 258 setType(i, readToken(r)); 259 } 260 r.readLine(); // swallow line end sequence 261 } 262 catch (IOException e) { 263 if (i == 0) { // assume if first read returns error, we have reached end of input 264 return false; 265 } 266 RuntimeException re = new RuntimeException("IO Error"); 267 re.initCause(e); 268 throw re; 269 } 270 271 return true; 272 } 273 274 public boolean read(ProgramElementDoc doc) { 275 276 // Doc. name 277 // Doc. isField, isMethod, isConstructor, isClass, isInterface 278 // ProgramElementDoc. containingClass, containingPackage 279 // ProgramElementDoc. isPublic, isProtected, isPrivate, isPackagePrivate 280 // ProgramElementDoc. isStatic, isFinal 281 // MemberDoc.isSynthetic 282 // ExecutableMemberDoc isSynchronized, signature 283 // Type.toString() // e.g. "String[][]" 284 // ClassDoc.isAbstract, superClass, interfaces, fields, methods, constructors, innerClasses 285 // FieldDoc type 286 // ConstructorDoc qualifiedName 287 // MethodDoc isAbstract, returnType 288 289 290 // status 291 setType(STA, tagStatus(doc)); 292 293 // visibility 294 if (doc.isPublic()) { 295 setType(VIS, VIS_PUBLIC); 296 } else if (doc.isProtected()) { 297 setType(VIS, VIS_PROTECTED); 298 } else if (doc.isPrivate()) { 299 setType(VIS, VIS_PRIVATE); 300 } else { 301 // default is package 302 } 303 304 // static 305 if (doc.isStatic()) { 306 setType(STK, STK_STATIC); 307 } else { 308 // default is non-static 309 } 310 311 // final 312 if (doc.isFinal()) { 313 setType(FIN, FIN_FINAL); 314 } else { 315 // default is non-final 316 } 317 318 // type 319 if (doc.isField()) { 320 setType(CAT, CAT_FIELD); 321 } else if (doc.isMethod()) { 322 setType(CAT, CAT_METHOD); 323 } else if (doc.isConstructor()) { 324 setType(CAT, CAT_CONSTRUCTOR); 325 } else if (doc.isClass() || doc.isInterface()) { 326 setType(CAT, CAT_CLASS); 327 } 328 329 setType(PAK, doc.containingPackage().name()); 330 setType(CLS, (doc.isClass() || doc.isInterface() || (doc.containingClass() == null)) ? "" : doc.containingClass().name()); 331 setType(NAM, doc.name()); 332 333 if (doc instanceof FieldDoc) { 334 FieldDoc fdoc = (FieldDoc)doc; 335 setType(SIG, fdoc.type().toString()); 336 } else if (doc instanceof ClassDoc) { 337 ClassDoc cdoc = (ClassDoc)doc; 338 339 if (cdoc.isClass() && cdoc.isAbstract()) { // interfaces are abstract by default, don't mark them as abstract 340 setType(ABS, ABS_ABSTRACT); 341 } 342 343 StringBuffer buf = new StringBuffer(); 344 if (cdoc.isClass()) { 345 buf.append("extends "); 346 buf.append(cdoc.superclass().qualifiedName()); 347 } 348 ClassDoc[] imp = cdoc.interfaces(); 349 if (imp != null && imp.length > 0) { 350 if (buf.length() > 0) { 351 buf.append(" "); 352 } 353 buf.append("implements"); 354 for (int i = 0; i < imp.length; ++i) { 355 if (i != 0) { 356 buf.append(","); 357 } 358 buf.append(" "); 359 buf.append(imp[i].qualifiedName()); 360 } 361 } 362 setType(SIG, buf.toString()); 363 } else { 364 ExecutableMemberDoc emdoc = (ExecutableMemberDoc)doc; 365 if (emdoc.isSynchronized()) { 366 setType(SYN, SYN_SYNCHRONIZED); 367 } 368 369 if (doc instanceof MethodDoc) { 370 MethodDoc mdoc = (MethodDoc)doc; 371 if (mdoc.isAbstract()) { 372 setType(ABS, ABS_ABSTRACT); 373 } 374 setType(SIG, mdoc.returnType().toString() + emdoc.signature()); 375 } else { 376 // constructor 377 setType(SIG, emdoc.signature()); 378 } 379 } 380 381 return true; 382 } 383 384 public static Comparator defaultComparator() { 385 final Comparator c = new Comparator() { 386 public int compare(Object lhs, Object rhs) { 387 Info lhi = (Info)lhs; 388 Info rhi = (Info)rhs; 389 int result = lhi.pack.compareTo(rhi.pack); 390 if (result == 0) { 391 result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name : lhi.cls) 392 .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name : rhi.cls); 393 if (result == 0) { 394 result = lhi.getVal(CAT)- rhi.getVal(CAT); 395 if (result == 0) { 396 result = lhi.name.compareTo(rhi.name); 397 if (result == 0) { 398 result = lhi.sig.compareTo(rhi.sig); 399 } 400 } 401 } 402 } 403 return result; 404 } 405 }; 406 return c; 407 } 408 409 public static Comparator changedComparator() { 410 final Comparator c = new Comparator() { 411 public int compare(Object lhs, Object rhs) { 412 Info lhi = (Info)lhs; 413 Info rhi = (Info)rhs; 414 int result = lhi.pack.compareTo(rhi.pack); 415 if (result == 0) { 416 result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name : lhi.cls) 417 .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name : rhi.cls); 418 if (result == 0) { 419 result = lhi.getVal(CAT)- rhi.getVal(CAT); 420 if (result == 0) { 421 result = lhi.name.compareTo(rhi.name); 422 if (result == 0 && lhi.getVal(CAT) != CAT_CLASS) { 423 result = lhi.sig.compareTo(rhi.sig); 424 } 425 } 426 } 427 } 428 return result; 429 } 430 }; 431 return c; 432 } 433 434 public static Comparator classFirstComparator() { 435 final Comparator c = new Comparator() { 436 public int compare(Object lhs, Object rhs) { 437 Info lhi = (Info)lhs; 438 Info rhi = (Info)rhs; 439 int result = lhi.pack.compareTo(rhi.pack); 440 if (result == 0) { 441 boolean lcls = lhi.getVal(CAT) == CAT_CLASS; 442 boolean rcls = rhi.getVal(CAT) == CAT_CLASS; 443 result = lcls == rcls ? 0 : (lcls ? -1 : 1); 444 if (result == 0) { 445 result = (lcls ? lhi.name : lhi.cls).compareTo(rcls ? rhi.name : rhi.cls); 446 if (result == 0) { 447 result = lhi.getVal(CAT)- rhi.getVal(CAT); 448 if (result == 0) { 449 result = lhi.name.compareTo(rhi.name); 450 if (result == 0 && !lcls) { 451 result = lhi.sig.compareTo(rhi.sig); 452 } 453 } 454 } 455 } 456 } 457 return result; 458 } 459 }; 460 return c; 461 } 462 463 private static final String[] typeNames = { 464 "status", "visibility", "static", "final", "synchronized", 465 "abstract", "category", "package", "class", "name", "signature" 466 }; 467 468 private static final String[][] names = { 469 { "draft ", "stable ", "deprecated", "obsolete " }, 470 { "package", "public", "protected", "private" }, 471 { "", "static" }, 472 { "", "final" }, 473 { "", "synchronized" }, 474 { "", "abstract" }, 475 { "class", "field", "constructor", "method" }, 476 null, 477 null, 478 null, 479 null, 480 null 481 }; 482 483 private static final String[][] shortNames = { 484 { "DR", "ST", "DP", "OB" }, 485 { "PK", "PB", "PT", "PR" }, 486 { "NS", "ST" }, 487 { "NF", "FN" }, 488 { "NS", "SY" }, 489 { "NA", "AB" }, 490 { "L", "F", "C", "M" }, 491 null, 492 null, 493 null, 494 null, 495 null 496 }; 497 498 private static void validateType(int typ) { 499 if (typ < 0 || typ > NUM_TYPES) { 500 throw new IllegalArgumentException("bad type index: " + typ); 501 } 502 } 503 504 public String toString() { 505 return get(NAM, true); 506 } 507 } 508 509 static final class DeltaInfo extends APIInfo { 510 private Info a; 511 private Info b; 512 513 DeltaInfo(Info a, Info b) { 514 this.a = a; 515 this.b = b; 516 } 517 518 public int getVal(int typ) { 519 return a.getVal(typ); 520 } 521 522 public String get(int typ, boolean brief) { 523 return a.get(typ, brief); 524 } 525 526 public void write(BufferedWriter w, boolean brief, boolean html, boolean detail) { 527 a.write(w, brief, html, detail); 528 try { 529 if (html) { 530 w.write("<br>"); 531 } 532 w.newLine(); 533 } 534 catch (Exception e) { 535 } 536 b.write(w, brief, html, detail); 537 } 538 539 public String toString() { 540 return a.get(NAM, true); 541 } 542 } 543 544 public static int optionLength(String option) { 545 if (option.equals("-html")) { 546 return 1; 547 } else if (option.equals("-name")) { 548 return 2; 549 } else if (option.equals("-output")) { 550 return 2; 551 } else if (option.equals("-compare")) { 552 return 2; 553 } 554 return 0; 555 } 556 557 public static boolean start(RootDoc root) { 558 return new CheckAPI(root).run(); 559 } 560 561 CheckAPI(RootDoc root) { 562 this.root = root; 563 564 // this.compare = "c:/doug/cvsproj/icu4j/src/com/ibm/icu/dev/tool/docs/api2_8.txt"; 565 566 String[][] options = root.options(); 567 for (int i = 0; i < options.length; ++i) { 568 String opt = options[i][0]; 569 if (opt.equals("-html")) { 570 this.html = true; 571 } else if (opt.equals("-name")) { 572 this.srcName = options[i][1]; 573 } else if (opt.equals("-output")) { 574 this.output = options[i][1]; 575 } else if (opt.equals("-compare")) { 576 this.compare = options[i][1]; 577 } 578 } 579 580 if (compare != null) { 581 try { 582 // URL url = new URL(compare); 583 File f = new File(compare); 584 InputStream is = new FileInputStream(f); 585 InputStreamReader isr = new InputStreamReader(is); 586 BufferedReader br = new BufferedReader(isr); 587 588 // read header line 589 /*int version = */Integer.parseInt(readToken(br)); 590 // check version if we change it later, probably can just rebuild though 591 this.compareName = readToken(br); 592 br.readLine(); 593 594 // read data 595 this.compareSet = new TreeSet(Info.defaultComparator()); 596 for (Info info = new Info(); info.read(br); info = new Info()) { 597 compareSet.add(info); 598 } 599 } 600 catch (Exception e) { 601 RuntimeException re = new RuntimeException("error reading " + compare); 602 re.initCause(e); 603 throw re; 604 } 605 } 606 607 results = new TreeSet(Info.defaultComparator()); 608 } 609 610 private boolean run() { 611 doDocs(root.classes()); 612 613 OutputStream os = System.out; 614 if (output != null) { 615 try { 616 os = new FileOutputStream(output); 617 } 618 catch (FileNotFoundException e) { 619 RuntimeException re = new RuntimeException(e.getMessage()); 620 re.initCause(e); 621 throw re; 622 } 623 } 624 625 BufferedWriter bw = null; 626 try { 627 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8"); 628 bw = new BufferedWriter(osw); 629 630 if (compareSet == null) { 631 // writing data file 632 bw.write(String.valueOf(DATA_FILE_VERSION) + SEP); // header version 633 bw.write(srcName + SEP); // source name 634 bw.newLine(); 635 writeResults(results, bw, true, false, false); 636 } else { 637 // writing comparison info 638 TreeSet removed = (TreeSet)compareSet.clone(); 639 removed.removeAll(results); 640 641 TreeSet added = (TreeSet)results.clone(); 642 added.removeAll(compareSet); 643 644 Iterator ai = added.iterator(); 645 Iterator ri = removed.iterator(); 646 ArrayList changed = new ArrayList(); 647 Comparator c = Info.changedComparator(); 648 Info a = null, r = null; 649 while (ai.hasNext() && ri.hasNext()) { 650 if (a == null) a = (Info)ai.next(); 651 if (r == null) r = (Info)ri.next(); 652 int result = c.compare(a, r); 653 if (result < 0) { 654 a = null; 655 } else if (result > 0) { 656 r = null; 657 } else { 658 changed.add(new DeltaInfo(a, r)); 659 a = null; ai.remove(); 660 r = null; ri.remove(); 661 } 662 } 663 664 added = stripAndResort(added); 665 removed = stripAndResort(removed); 666 667 if (html) { 668 String title = "ICU4J API Comparison: " + srcName + " with " + compareName; 669 670 bw.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); 671 bw.newLine(); 672 bw.write("<html>"); 673 bw.newLine(); 674 bw.write("<head>"); 675 bw.newLine(); 676 bw.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"); 677 bw.newLine(); 678 bw.write("<title>"); 679 bw.write(title); 680 bw.write("</title>"); 681 bw.newLine(); 682 bw.write("<body>"); 683 bw.newLine(); 684 685 bw.write("<h1>"); 686 bw.write(title); 687 bw.write("</h1>"); 688 bw.newLine(); 689 690 bw.write("<hr/>"); 691 bw.newLine(); 692 bw.write("<h2>"); 693 bw.write("Removed from " + compareName); 694 bw.write("</h2>"); 695 bw.newLine(); 696 697 if (removed.size() > 0) { 698 writeResults(removed, bw, false, true, false); 699 } else { 700 bw.write("<p>(no API removed)</p>"); 701 } 702 bw.newLine(); 703 704 bw.write("<hr/>"); 705 bw.newLine(); 706 bw.write("<h2>"); 707 bw.write("Changed in " + srcName); 708 bw.write("</h2>"); 709 bw.newLine(); 710 711 if (changed.size() > 0) { 712 writeResults(changed, bw, false, true, true); 713 } else { 714 bw.write("<p>(no API changed)</p>"); 715 } 716 bw.newLine(); 717 718 bw.write("<hr/>"); 719 bw.newLine(); 720 bw.write("<h2>"); 721 bw.write("Added in " + srcName); 722 bw.write("</h2>"); 723 bw.newLine(); 724 725 if (added.size() > 0) { 726 writeResults(added, bw, false, true, false); 727 } else { 728 bw.write("<p>(no API added)</p>"); 729 } 730 bw.write("<hr/>"); 731 bw.newLine(); 732 bw.write("<p><i>Contents generated by CheckAPI tool.<br/>Copyright (C) 2004, International Business Machines Corporation, All Rights Reserved.</i></p>"); 733 bw.newLine(); 734 bw.write("</body>"); 735 bw.newLine(); 736 bw.write("</html>"); 737 bw.newLine(); 738 } else { 739 bw.write("Comparing " + srcName + " with " + compareName); 740 bw.newLine(); 741 bw.newLine(); 742 743 bw.newLine(); 744 bw.write("=== Removed from " + compareName + " ==="); 745 bw.newLine(); 746 if (removed.size() > 0) { 747 writeResults(removed, bw, false, false, false); 748 } else { 749 bw.write("(no API removed)"); 750 } 751 bw.newLine(); 752 753 bw.newLine(); 754 bw.write("=== Changed in " + srcName + " ==="); 755 bw.newLine(); 756 if (changed.size() > 0) { 757 writeResults(changed, bw, false, false, true); 758 } else { 759 bw.write("(no API changed)"); 760 } 761 bw.newLine(); 762 763 bw.newLine(); 764 bw.write("=== Added in " + srcName + " ==="); 765 bw.newLine(); 766 if (added.size() > 0) { 767 writeResults(added, bw, false, false, false); 768 } else { 769 bw.write("(no API added)"); 770 } 771 bw.newLine(); 772 } 773 } 774 775 bw.close(); 776 } catch (IOException e) { 777 try { bw.close(); } catch (IOException e2) {} 778 RuntimeException re = new RuntimeException("write error: " + e.getMessage()); 779 re.initCause(e); 780 throw re; 781 } 782 783 return false; 784 } 785 786 private void doDocs(ProgramElementDoc[] docs) { 787 if (docs != null && docs.length > 0) { 788 for (int i = 0; i < docs.length; ++i) { 789 doDoc(docs[i]); 790 } 791 } 792 } 793 794 private void doDoc(ProgramElementDoc doc) { 795 if (ignore(doc)) return; 796 797 if (doc.isClass() || doc.isInterface()) { 798 ClassDoc cdoc = (ClassDoc)doc; 799 doDocs(cdoc.fields()); 800 doDocs(cdoc.constructors()); 801 doDocs(cdoc.methods()); 802 doDocs(cdoc.innerClasses()); 803 } 804 805 Info info = new Info(); 806 if (info.read(doc)) { 807 results.add(info); 808 } 809 } 810 811 private boolean ignore(ProgramElementDoc doc) { 812 if (doc == null) return true; 813 if (doc.isPrivate() || doc.isPackagePrivate()) return true; 814 if (doc instanceof ConstructorDoc && ((ConstructorDoc)doc).isSynthetic()) return true; 815 if (doc.qualifiedName().indexOf(".misc") != -1) return true; 816 Tag[] tags = doc.tags(); 817 for (int i = 0; i < tags.length; ++i) { 818 if (tagKindIndex(tags[i].kind()) == INTERNAL) return true; 819 } 820 821 return false; 822 } 823 824 private static void writeResults(Collection c, BufferedWriter w, boolean brief, boolean html, boolean detail) { 825 Iterator iter = c.iterator(); 826 String pack = null; 827 String clas = null; 828 while (iter.hasNext()) { 829 APIInfo info = (APIInfo)iter.next(); 830 if (brief) { 831 info.write(w, brief, false, detail); 832 } else { 833 try { 834 String p = info.get(PAK, true); 835 if (!p.equals(pack)) { 836 w.newLine(); 837 if (html) { 838 if (clas != null) { 839 w.write("</ul>"); 840 w.newLine(); 841 } 842 if (pack != null) { 843 w.write("</ul>"); 844 w.newLine(); 845 } 846 847 w.write("<h3>Package "); 848 w.write(p); 849 w.write("</h3>"); 850 w.newLine(); 851 w.write("<ul>"); 852 w.newLine(); 853 } else { 854 w.write("Package "); 855 w.write(p); 856 w.write(':'); 857 } 858 w.newLine(); 859 w.newLine(); 860 861 pack = p; 862 clas = null; 863 } 864 865 if (info.getVal(CAT) != CAT_CLASS) { 866 String name = info.get(CLS, true); 867 if (!name.equals(clas)) { 868 if (html) { 869 if (clas != null) { 870 w.write("</ul>"); 871 } 872 w.write("<li>"); 873 w.write(name); 874 w.newLine(); 875 w.write("<ul>"); 876 } else { 877 w.write(name); 878 w.newLine(); 879 } 880 clas = name; 881 } 882 w.write(" "); 883 } 884 if (html) { 885 w.write("<li>"); 886 info.write(w, brief, html, detail); 887 w.write("</li>"); 888 } else { 889 info.write(w, brief, html, detail); 890 } 891 } 892 catch (IOException e) { 893 System.err.println("IOException " + e.getMessage() + " writing " + info); 894 } 895 } 896 } 897 if (html) { 898 try { 899 if (clas != null) { 900 w.write("</ul>"); 901 w.newLine(); 902 } 903 if (pack != null) { 904 w.write("</ul>"); 905 w.newLine(); 906 } 907 } 908 catch (IOException e) { 909 } 910 } 911 } 912 913 private static String readToken(BufferedReader r) throws IOException { 914 char[] buf = new char[256]; 915 int i = 0; 916 for (; i < buf.length; ++i) { 917 int c = r.read(); 918 if (c == -1) { 919 throw new IOException("unexpected EOF"); 920 } else if (c == SEP) { 921 break; 922 } 923 buf[i] = (char)c; 924 } 925 if (i == buf.length) { 926 throw new IOException("unterminated token" + new String(buf)); 927 } 928 929 return new String(buf, 0, i); 930 } 931 932 private static TreeSet stripAndResort(TreeSet t) { 933 stripClassInfo(t); 934 TreeSet r = new TreeSet(Info.classFirstComparator()); 935 r.addAll(t); 936 return r; 937 } 938 939 private static void stripClassInfo(Collection c) { 940 // c is sorted with class info first 941 Iterator iter = c.iterator(); 942 String cname = null; 943 while (iter.hasNext()) { 944 Info info = (Info)iter.next(); 945 String cls = info.get(CLS, true); 946 if (cname != null) { 947 if (cname.equals(cls)) { 948 iter.remove(); 949 continue; 950 } 951 cname = null; 952 } 953 if (info.getVal(CAT) == CAT_CLASS) { 954 cname = info.get(NAM, true); 955 } 956 } 957 } 958 959 private static int tagStatus(final Doc doc) { 960 class Result { 961 int res = -1; 962 void set(int val) { if (res != -1) throw new RuntimeException("bad doc: " + doc); res = val; } 963 int get() { 964 if (res == -1) { 965 System.err.println("warning: no tag for " + doc); 966 return 0; 967 } 968 return res; 969 } 970 } 971 972 Tag[] tags = doc.tags(); 973 Result result = new Result(); 974 for (int i = 0; i < tags.length; ++i) { 975 Tag tag = tags[i]; 976 977 String kind = tag.kind(); 978 int ix = tagKindIndex(kind); 979 980 switch (ix) { 981 case INTERNAL: 982 result.set(-2); 983 break; 984 985 case DRAFT: 986 result.set(STA_DRAFT); 987 break; 988 989 case STABLE: 990 result.set(STA_STABLE); 991 break; 992 993 case DEPRECATED: 994 result.set(STA_DEPRECATED); 995 break; 996 997 case OBSOLETE: 998 result.set(STA_OBSOLETE); 999 break; 1000 1001 case SINCE: 1002 case EXCEPTION: 1003 case VERSION: 1004 case UNKNOWN: 1005 case AUTHOR: 1006 case SEE: 1007 case PARAM: 1008 case RETURN: 1009 case THROWS: 1010 case SERIAL: 1011 break; 1012 1013 default: 1014 throw new RuntimeException("unknown index " + ix + " for tag: " + kind); 1015 } 1016 } 1017 1018 return result.get(); 1019 } 1020 1021 private static final int UNKNOWN = -1; 1022 private static final int INTERNAL = 0; 1023 private static final int DRAFT = 1; 1024 private static final int STABLE = 2; 1025 private static final int SINCE = 3; 1026 private static final int DEPRECATED = 4; 1027 private static final int AUTHOR = 5; 1028 private static final int SEE = 6; 1029 private static final int VERSION = 7; 1030 private static final int PARAM = 8; 1031 private static final int RETURN = 9; 1032 private static final int THROWS = 10; 1033 private static final int OBSOLETE = 11; 1034 private static final int EXCEPTION = 12; 1035 private static final int SERIAL = 13; 1036 1037 private static int tagKindIndex(String kind) { 1038 final String[] tagKinds = { 1039 "@internal", "@draft", "@stable", "@since", "@deprecated", "@author", "@see", "@version", 1040 "@param", "@return", "@throws", "@obsolete", "@exception", "@serial" 1041 }; 1042 1043 for (int i = 0; i < tagKinds.length; ++i) { 1044 if (kind.equals(tagKinds[i])) { 1045 return i; 1046 } 1047 } 1048 return UNKNOWN; 1049 } 1050 } 1051