0001: /**
0002: *******************************************************************************
0003: * Copyright (C) 2004-2005, International Business Machines Corporation and *
0004: * others. All Rights Reserved. *
0005: *******************************************************************************
0006: */
0007:
0008: /**
0009: * Generate a list of ICU's public APIs, sorted by qualified name and signature
0010: * public APIs are all non-internal, non-package apis in com.ibm.icu.[lang|math|text|util].
0011: * For each API, list
0012: * - public, package, protected, or private (PB PK PT PR)
0013: * - static or non-static (STK NST)
0014: * - final or non-final (FN NF)
0015: * - synchronized or non-synchronized (SYN NSY)
0016: * - stable, draft, deprecated, obsolete (ST DR DP OB)
0017: * - abstract or non-abstract (AB NA)
0018: * - constructor, member, field (C M F)
0019: *
0020: * Requires JDK 1.4.2 or later
0021: *
0022: * Sample invocation:
0023: * c:/j2sdk1.4.2/bin/javadoc
0024: * -classpath c:/jd2sk1.4.2/lib/tools.jar
0025: * -doclet com.ibm.icu.dev.tool.docs.CheckAPI
0026: * -docletpath c:/doug/cvsproj/icu4j/src
0027: * -sourcepath c:/eclipse2.1/workspace2/icu4j/src
0028: * -compare c:/doug/cvsproj/icu4j/src/com/ibm/icu/dev/tool/docs/api2_6_1.txt
0029: * -output foo
0030: * com.ibm.icu.text
0031: *
0032: * todo: separate generation of data files (which requires taglet) from
0033: * comparison and report generation (which does not require it)
0034: * todo: provide command-line control of filters of which subclasses/packages to process
0035: * todo: record full inheritance heirarchy, not just immediate inheritance
0036: * todo: allow for aliasing comparisons (force (pkg.)*class to be treated as though it
0037: * were in a different pkg/class heirarchy (facilitates comparison of icu4j and java)
0038: */package com.ibm.icu.dev.tool.docs;
0039:
0040: import com.sun.javadoc.*;
0041: import java.io.*;
0042: import java.util.*;
0043:
0044: public class CheckAPI {
0045: RootDoc root;
0046: String compare; // file
0047: String compareName;
0048: TreeSet compareSet;
0049: TreeSet results;
0050: boolean html;
0051: String srcName = "Current"; // default source name
0052: String output;
0053:
0054: private static final int DATA_FILE_VERSION = 1;
0055: private static final char SEP = ';';
0056:
0057: private static final int STA = 0, STA_DRAFT = 0, STA_STABLE = 1,
0058: STA_DEPRECATED = 2, STA_OBSOLETE = 3;
0059: private static final int VIS = 1, VIS_PACKAGE = 0, VIS_PUBLIC = 1,
0060: VIS_PROTECTED = 2, VIS_PRIVATE = 3;
0061: private static final int STK = 2, STK_STATIC = 1;
0062: private static final int FIN = 3, FIN_FINAL = 1;
0063: private static final int SYN = 4, SYN_SYNCHRONIZED = 1;
0064: private static final int ABS = 5, ABS_ABSTRACT = 1;
0065: private static final int CAT = 6, CAT_CLASS = 0, CAT_FIELD = 1,
0066: CAT_CONSTRUCTOR = 2, CAT_METHOD = 3;
0067: private static final int PAK = 7;
0068: private static final int CLS = 8;
0069: private static final int NAM = 9;
0070: private static final int SIG = 10;
0071: private static final int EXC = 11;
0072: private static final int NUM_TYPES = 11;
0073:
0074: static abstract class APIInfo {
0075: public abstract int getVal(int typ);
0076:
0077: public abstract String get(int typ, boolean brief);
0078:
0079: public abstract void write(BufferedWriter w, boolean brief,
0080: boolean html, boolean detail);
0081: }
0082:
0083: final static class Info extends APIInfo {
0084: private int info;
0085: private String pack; // package
0086: private String cls; // enclosing class
0087: private String name; // name
0088: private String sig; // signature, class: inheritance, method: signature, field: type, const: signature
0089: private String exc; // throws
0090:
0091: public int getVal(int typ) {
0092: validateType(typ);
0093: return (info >> (typ * 2)) & 0x3;
0094: }
0095:
0096: public String get(int typ, boolean brief) {
0097: validateType(typ);
0098: String[] vals = brief ? shortNames[typ] : names[typ];
0099: if (vals == null) {
0100: switch (typ) {
0101: case PAK:
0102: return pack;
0103: case CLS:
0104: return cls;
0105: case NAM:
0106: return name;
0107: case SIG:
0108: return sig;
0109: case EXC:
0110: return exc;
0111: }
0112: }
0113: int val = (info >> (typ * 2)) & 0x3;
0114: return vals[val];
0115: }
0116:
0117: private void setType(int typ, int val) {
0118: validateType(typ);
0119: info &= ~(0x3 << (typ * 2));
0120: info |= (val & 0x3) << (typ * 2);
0121: }
0122:
0123: private void setType(int typ, String val) {
0124: validateType(typ);
0125: String[] vals = shortNames[typ];
0126: if (vals == null) {
0127: switch (typ) {
0128: case PAK:
0129: pack = val;
0130: break;
0131: case CLS:
0132: cls = val;
0133: break;
0134: case NAM:
0135: name = val;
0136: break;
0137: case SIG:
0138: sig = val;
0139: break;
0140: case EXC:
0141: exc = val;
0142: break;
0143: }
0144: return;
0145: }
0146:
0147: for (int i = 0; i < vals.length; ++i) {
0148: if (val.equalsIgnoreCase(vals[i])) {
0149: info &= ~(0x3 << (typ * 2));
0150: info |= i << (typ * 2);
0151: return;
0152: }
0153: }
0154:
0155: throw new IllegalArgumentException("unrecognized value '"
0156: + val + "' for type '" + typeNames[typ] + "'");
0157: }
0158:
0159: public void write(BufferedWriter w, boolean brief,
0160: boolean html, boolean detail) {
0161: try {
0162: if (brief) {
0163: for (int i = 0; i < NUM_TYPES; ++i) {
0164: String s = get(i, true);
0165: if (s != null) {
0166: w.write(s);
0167: }
0168: w.write(SEP);
0169: }
0170: } else {
0171: // remove all occurrences of icu packages from the param string
0172: // fortunately, all the packages have 4 chars (lang, math, text, util).
0173: String xsig = sig;
0174: if (!detail) {
0175: final String ICUPACK = "com.ibm.icu.";
0176: StringBuffer buf = new StringBuffer();
0177: for (int i = 0; i < sig.length();) {
0178: int n = sig.indexOf(ICUPACK, i);
0179: if (n == -1) {
0180: buf.append(sig.substring(i));
0181: break;
0182: }
0183: buf.append(sig.substring(i, n));
0184: i = n + ICUPACK.length() + 5; // trailing 'xxxx.'
0185: }
0186: xsig = buf.toString();
0187: }
0188:
0189: // construct signature
0190: for (int i = STA; i < CAT; ++i) { // include status
0191: String s = get(i, false);
0192: if (s != null && s.length() > 0) {
0193: if (i == STA) {
0194: w.write('(');
0195: w.write(s);
0196: w.write(')');
0197: } else {
0198: w.write(s);
0199: }
0200: w.write(' ');
0201: }
0202: }
0203:
0204: int val = getVal(CAT);
0205: switch (val) {
0206: case CAT_CLASS:
0207: if (sig.indexOf("extends") == -1) {
0208: w.write("interface ");
0209: } else {
0210: w.write("class ");
0211: }
0212: if (cls.length() > 0) {
0213: w.write(cls);
0214: w.write('.');
0215: }
0216: w.write(name);
0217: if (detail) {
0218: w.write(' ');
0219: w.write(sig);
0220: }
0221: break;
0222:
0223: case CAT_FIELD:
0224: w.write(xsig);
0225: w.write(' ');
0226: w.write(name);
0227: break;
0228:
0229: case CAT_METHOD:
0230: case CAT_CONSTRUCTOR:
0231: int n = xsig.indexOf('(');
0232: if (n > 0) {
0233: w.write(xsig.substring(0, n));
0234: w.write(' ');
0235: } else {
0236: n = 0;
0237: }
0238: w.write(name);
0239: w.write(xsig.substring(n));
0240: break;
0241: }
0242: }
0243: w.newLine();
0244: } catch (IOException e) {
0245: RuntimeException re = new RuntimeException("IO Error");
0246: re.initCause(e);
0247: throw re;
0248: }
0249: }
0250:
0251: public boolean read(BufferedReader r) {
0252: int i = 0;
0253: try {
0254: for (; i < NUM_TYPES; ++i) {
0255: setType(i, readToken(r));
0256: }
0257: r.readLine(); // swallow line end sequence
0258: } catch (IOException e) {
0259: if (i == 0) { // assume if first read returns error, we have reached end of input
0260: return false;
0261: }
0262: RuntimeException re = new RuntimeException("IO Error");
0263: re.initCause(e);
0264: throw re;
0265: }
0266:
0267: return true;
0268: }
0269:
0270: public boolean read(ProgramElementDoc doc) {
0271:
0272: // Doc. name
0273: // Doc. isField, isMethod, isConstructor, isClass, isInterface
0274: // ProgramElementDoc. containingClass, containingPackage
0275: // ProgramElementDoc. isPublic, isProtected, isPrivate, isPackagePrivate
0276: // ProgramElementDoc. isStatic, isFinal
0277: // MemberDoc.isSynthetic
0278: // ExecutableMemberDoc isSynchronized, signature
0279: // Type.toString() // e.g. "String[][]"
0280: // ClassDoc.isAbstract, superClass, interfaces, fields, methods, constructors, innerClasses
0281: // FieldDoc type
0282: // ConstructorDoc qualifiedName
0283: // MethodDoc isAbstract, returnType
0284:
0285: // status
0286: setType(STA, tagStatus(doc));
0287:
0288: // visibility
0289: if (doc.isPublic()) {
0290: setType(VIS, VIS_PUBLIC);
0291: } else if (doc.isProtected()) {
0292: setType(VIS, VIS_PROTECTED);
0293: } else if (doc.isPrivate()) {
0294: setType(VIS, VIS_PRIVATE);
0295: } else {
0296: // default is package
0297: }
0298:
0299: // static
0300: if (doc.isStatic()) {
0301: setType(STK, STK_STATIC);
0302: } else {
0303: // default is non-static
0304: }
0305:
0306: // final
0307: if (doc.isFinal()) {
0308: setType(FIN, FIN_FINAL);
0309: } else {
0310: // default is non-final
0311: }
0312:
0313: // type
0314: if (doc.isField()) {
0315: setType(CAT, CAT_FIELD);
0316: } else if (doc.isMethod()) {
0317: setType(CAT, CAT_METHOD);
0318: } else if (doc.isConstructor()) {
0319: setType(CAT, CAT_CONSTRUCTOR);
0320: } else if (doc.isClass() || doc.isInterface()) {
0321: setType(CAT, CAT_CLASS);
0322: }
0323:
0324: setType(PAK, doc.containingPackage().name());
0325: setType(CLS, (doc.isClass() || doc.isInterface() || (doc
0326: .containingClass() == null)) ? "" : doc
0327: .containingClass().name());
0328: setType(NAM, doc.name());
0329:
0330: if (doc instanceof FieldDoc) {
0331: FieldDoc fdoc = (FieldDoc) doc;
0332: setType(SIG, fdoc.type().toString());
0333: } else if (doc instanceof ClassDoc) {
0334: ClassDoc cdoc = (ClassDoc) doc;
0335:
0336: if (cdoc.isClass() && cdoc.isAbstract()) { // interfaces are abstract by default, don't mark them as abstract
0337: setType(ABS, ABS_ABSTRACT);
0338: }
0339:
0340: StringBuffer buf = new StringBuffer();
0341: if (cdoc.isClass()) {
0342: buf.append("extends ");
0343: buf.append(cdoc.super class().qualifiedName());
0344: }
0345: ClassDoc[] imp = cdoc.interfaces();
0346: if (imp != null && imp.length > 0) {
0347: if (buf.length() > 0) {
0348: buf.append(" ");
0349: }
0350: buf.append("implements");
0351: for (int i = 0; i < imp.length; ++i) {
0352: if (i != 0) {
0353: buf.append(",");
0354: }
0355: buf.append(" ");
0356: buf.append(imp[i].qualifiedName());
0357: }
0358: }
0359: setType(SIG, buf.toString());
0360: } else {
0361: ExecutableMemberDoc emdoc = (ExecutableMemberDoc) doc;
0362: if (emdoc.isSynchronized()) {
0363: setType(SYN, SYN_SYNCHRONIZED);
0364: }
0365:
0366: if (doc instanceof MethodDoc) {
0367: MethodDoc mdoc = (MethodDoc) doc;
0368: if (mdoc.isAbstract()) {
0369: setType(ABS, ABS_ABSTRACT);
0370: }
0371: setType(SIG, mdoc.returnType().toString()
0372: + emdoc.signature());
0373: } else {
0374: // constructor
0375: setType(SIG, emdoc.signature());
0376: }
0377: }
0378:
0379: return true;
0380: }
0381:
0382: public static Comparator defaultComparator() {
0383: final Comparator c = new Comparator() {
0384: public int compare(Object lhs, Object rhs) {
0385: Info lhi = (Info) lhs;
0386: Info rhi = (Info) rhs;
0387: int result = lhi.pack.compareTo(rhi.pack);
0388: if (result == 0) {
0389: result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name
0390: : lhi.cls)
0391: .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name
0392: : rhi.cls);
0393: if (result == 0) {
0394: result = lhi.getVal(CAT) - rhi.getVal(CAT);
0395: if (result == 0) {
0396: result = lhi.name.compareTo(rhi.name);
0397: if (result == 0) {
0398: result = lhi.sig.compareTo(rhi.sig);
0399: }
0400: }
0401: }
0402: }
0403: return result;
0404: }
0405: };
0406: return c;
0407: }
0408:
0409: public static Comparator changedComparator() {
0410: final Comparator c = new Comparator() {
0411: public int compare(Object lhs, Object rhs) {
0412: Info lhi = (Info) lhs;
0413: Info rhi = (Info) rhs;
0414: int result = lhi.pack.compareTo(rhi.pack);
0415: if (result == 0) {
0416: result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name
0417: : lhi.cls)
0418: .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name
0419: : rhi.cls);
0420: if (result == 0) {
0421: result = lhi.getVal(CAT) - rhi.getVal(CAT);
0422: if (result == 0) {
0423: result = lhi.name.compareTo(rhi.name);
0424: if (result == 0
0425: && lhi.getVal(CAT) != CAT_CLASS) {
0426: result = lhi.sig.compareTo(rhi.sig);
0427: }
0428: }
0429: }
0430: }
0431: return result;
0432: }
0433: };
0434: return c;
0435: }
0436:
0437: public static Comparator classFirstComparator() {
0438: final Comparator c = new Comparator() {
0439: public int compare(Object lhs, Object rhs) {
0440: Info lhi = (Info) lhs;
0441: Info rhi = (Info) rhs;
0442: int result = lhi.pack.compareTo(rhi.pack);
0443: if (result == 0) {
0444: boolean lcls = lhi.getVal(CAT) == CAT_CLASS;
0445: boolean rcls = rhi.getVal(CAT) == CAT_CLASS;
0446: result = lcls == rcls ? 0 : (lcls ? -1 : 1);
0447: if (result == 0) {
0448: result = (lcls ? lhi.name : lhi.cls)
0449: .compareTo(rcls ? rhi.name
0450: : rhi.cls);
0451: if (result == 0) {
0452: result = lhi.getVal(CAT)
0453: - rhi.getVal(CAT);
0454: if (result == 0) {
0455: result = lhi.name
0456: .compareTo(rhi.name);
0457: if (result == 0 && !lcls) {
0458: result = lhi.sig
0459: .compareTo(rhi.sig);
0460: }
0461: }
0462: }
0463: }
0464: }
0465: return result;
0466: }
0467: };
0468: return c;
0469: }
0470:
0471: private static final String[] typeNames = { "status",
0472: "visibility", "static", "final", "synchronized",
0473: "abstract", "category", "package", "class", "name",
0474: "signature" };
0475:
0476: private static final String[][] names = {
0477: { "draft ", "stable ", "deprecated",
0478: "obsolete " },
0479: { "package", "public", "protected", "private" },
0480: { "", "static" }, { "", "final" },
0481: { "", "synchronized" }, { "", "abstract" },
0482: { "class", "field", "constructor", "method" }, null,
0483: null, null, null, null };
0484:
0485: private static final String[][] shortNames = {
0486: { "DR", "ST", "DP", "OB" }, { "PK", "PB", "PT", "PR" },
0487: { "NS", "ST" }, { "NF", "FN" }, { "NS", "SY" },
0488: { "NA", "AB" }, { "L", "F", "C", "M" }, null, null,
0489: null, null, null };
0490:
0491: private static void validateType(int typ) {
0492: if (typ < 0 || typ > NUM_TYPES) {
0493: throw new IllegalArgumentException("bad type index: "
0494: + typ);
0495: }
0496: }
0497:
0498: public String toString() {
0499: return get(NAM, true);
0500: }
0501: }
0502:
0503: static final class DeltaInfo extends APIInfo {
0504: private Info a;
0505: private Info b;
0506:
0507: DeltaInfo(Info a, Info b) {
0508: this .a = a;
0509: this .b = b;
0510: }
0511:
0512: public int getVal(int typ) {
0513: return a.getVal(typ);
0514: }
0515:
0516: public String get(int typ, boolean brief) {
0517: return a.get(typ, brief);
0518: }
0519:
0520: public void write(BufferedWriter w, boolean brief,
0521: boolean html, boolean detail) {
0522: a.write(w, brief, html, detail);
0523: try {
0524: if (html) {
0525: w.write("<br>");
0526: }
0527: w.newLine();
0528: } catch (Exception e) {
0529: }
0530: b.write(w, brief, html, detail);
0531: }
0532:
0533: public String toString() {
0534: return a.get(NAM, true);
0535: }
0536: }
0537:
0538: public static int optionLength(String option) {
0539: if (option.equals("-html")) {
0540: return 1;
0541: } else if (option.equals("-name")) {
0542: return 2;
0543: } else if (option.equals("-output")) {
0544: return 2;
0545: } else if (option.equals("-compare")) {
0546: return 2;
0547: }
0548: return 0;
0549: }
0550:
0551: public static boolean start(RootDoc root) {
0552: return new CheckAPI(root).run();
0553: }
0554:
0555: CheckAPI(RootDoc root) {
0556: this .root = root;
0557:
0558: // this.compare = "c:/doug/cvsproj/icu4j/src/com/ibm/icu/dev/tool/docs/api2_8.txt";
0559:
0560: String[][] options = root.options();
0561: for (int i = 0; i < options.length; ++i) {
0562: String opt = options[i][0];
0563: if (opt.equals("-html")) {
0564: this .html = true;
0565: } else if (opt.equals("-name")) {
0566: this .srcName = options[i][1];
0567: } else if (opt.equals("-output")) {
0568: this .output = options[i][1];
0569: } else if (opt.equals("-compare")) {
0570: this .compare = options[i][1];
0571: }
0572: }
0573:
0574: if (compare != null) {
0575: try {
0576: // URL url = new URL(compare);
0577: File f = new File(compare);
0578: InputStream is = new FileInputStream(f);
0579: InputStreamReader isr = new InputStreamReader(is);
0580: BufferedReader br = new BufferedReader(isr);
0581:
0582: // read header line
0583: int version = Integer.parseInt(readToken(br));
0584: // check version if we change it later, probably can just rebuild though
0585: this .compareName = readToken(br);
0586: br.readLine();
0587:
0588: // read data
0589: this .compareSet = new TreeSet(Info.defaultComparator());
0590: for (Info info = new Info(); info.read(br); info = new Info()) {
0591: compareSet.add(info);
0592: }
0593: } catch (Exception e) {
0594: RuntimeException re = new RuntimeException(
0595: "error reading " + compare);
0596: re.initCause(e);
0597: throw re;
0598: }
0599: }
0600:
0601: results = new TreeSet(Info.defaultComparator());
0602: }
0603:
0604: private boolean run() {
0605: doDocs(root.classes());
0606:
0607: OutputStream os = System.out;
0608: if (output != null) {
0609: try {
0610: os = new FileOutputStream(output);
0611: } catch (FileNotFoundException e) {
0612: RuntimeException re = new RuntimeException(e
0613: .getMessage());
0614: re.initCause(e);
0615: throw re;
0616: }
0617: }
0618:
0619: BufferedWriter bw = null;
0620: try {
0621: OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
0622: bw = new BufferedWriter(osw);
0623:
0624: if (compareSet == null) {
0625: // writing data file
0626: bw.write(String.valueOf(DATA_FILE_VERSION) + SEP); // header version
0627: bw.write(srcName + SEP); // source name
0628: bw.newLine();
0629: writeResults(results, bw, true, false, false);
0630: } else {
0631: // writing comparison info
0632: TreeSet removed = (TreeSet) compareSet.clone();
0633: removed.removeAll(results);
0634:
0635: TreeSet added = (TreeSet) results.clone();
0636: added.removeAll(compareSet);
0637:
0638: Iterator ai = added.iterator();
0639: Iterator ri = removed.iterator();
0640: ArrayList changed = new ArrayList();
0641: Comparator c = Info.changedComparator();
0642: Info a = null, r = null;
0643: while (ai.hasNext() && ri.hasNext()) {
0644: if (a == null)
0645: a = (Info) ai.next();
0646: if (r == null)
0647: r = (Info) ri.next();
0648: int result = c.compare(a, r);
0649: if (result < 0) {
0650: a = null;
0651: } else if (result > 0) {
0652: r = null;
0653: } else {
0654: changed.add(new DeltaInfo(a, r));
0655: a = null;
0656: ai.remove();
0657: r = null;
0658: ri.remove();
0659: }
0660: }
0661:
0662: added = stripAndResort(added);
0663: removed = stripAndResort(removed);
0664:
0665: if (html) {
0666: String title = "ICU4J API Comparison: " + srcName
0667: + " with " + compareName;
0668:
0669: bw
0670: .write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
0671: bw.newLine();
0672: bw.write("<html>");
0673: bw.newLine();
0674: bw.write("<head>");
0675: bw.newLine();
0676: bw
0677: .write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
0678: bw.newLine();
0679: bw.write("<title>");
0680: bw.write(title);
0681: bw.write("</title>");
0682: bw.newLine();
0683: bw.write("<body>");
0684: bw.newLine();
0685:
0686: bw.write("<h1>");
0687: bw.write(title);
0688: bw.write("</h1>");
0689: bw.newLine();
0690:
0691: bw.write("<hr/>");
0692: bw.newLine();
0693: bw.write("<h2>");
0694: bw.write("Removed from " + compareName);
0695: bw.write("</h2>");
0696: bw.newLine();
0697:
0698: if (removed.size() > 0) {
0699: writeResults(removed, bw, false, true, false);
0700: } else {
0701: bw.write("<p>(no API removed)</p>");
0702: }
0703: bw.newLine();
0704:
0705: bw.write("<hr/>");
0706: bw.newLine();
0707: bw.write("<h2>");
0708: bw.write("Changed in " + srcName);
0709: bw.write("</h2>");
0710: bw.newLine();
0711:
0712: if (changed.size() > 0) {
0713: writeResults(changed, bw, false, true, true);
0714: } else {
0715: bw.write("<p>(no API changed)</p>");
0716: }
0717: bw.newLine();
0718:
0719: bw.write("<hr/>");
0720: bw.newLine();
0721: bw.write("<h2>");
0722: bw.write("Added in " + srcName);
0723: bw.write("</h2>");
0724: bw.newLine();
0725:
0726: if (added.size() > 0) {
0727: writeResults(added, bw, false, true, false);
0728: } else {
0729: bw.write("<p>(no API added)</p>");
0730: }
0731: bw.write("<hr/>");
0732: bw.newLine();
0733: bw
0734: .write("<p><i>Contents generated by CheckAPI tool.<br/>Copyright (C) 2004, International Business Machines Corporation, All Rights Reserved.</i></p>");
0735: bw.newLine();
0736: bw.write("</body>");
0737: bw.newLine();
0738: bw.write("</html>");
0739: bw.newLine();
0740: } else {
0741: bw.write("Comparing " + srcName + " with "
0742: + compareName);
0743: bw.newLine();
0744: bw.newLine();
0745:
0746: bw.newLine();
0747: bw
0748: .write("=== Removed from " + compareName
0749: + " ===");
0750: bw.newLine();
0751: if (removed.size() > 0) {
0752: writeResults(removed, bw, false, false, false);
0753: } else {
0754: bw.write("(no API removed)");
0755: }
0756: bw.newLine();
0757:
0758: bw.newLine();
0759: bw.write("=== Changed in " + srcName + " ===");
0760: bw.newLine();
0761: if (changed.size() > 0) {
0762: writeResults(changed, bw, false, false, true);
0763: } else {
0764: bw.write("(no API changed)");
0765: }
0766: bw.newLine();
0767:
0768: bw.newLine();
0769: bw.write("=== Added in " + srcName + " ===");
0770: bw.newLine();
0771: if (added.size() > 0) {
0772: writeResults(added, bw, false, false, false);
0773: } else {
0774: bw.write("(no API added)");
0775: }
0776: bw.newLine();
0777: }
0778: }
0779:
0780: bw.close();
0781: } catch (IOException e) {
0782: try {
0783: bw.close();
0784: } catch (IOException e2) {
0785: }
0786: RuntimeException re = new RuntimeException("write error: "
0787: + e.getMessage());
0788: re.initCause(e);
0789: throw re;
0790: }
0791:
0792: return false;
0793: }
0794:
0795: private void doDocs(ProgramElementDoc[] docs) {
0796: if (docs != null && docs.length > 0) {
0797: for (int i = 0; i < docs.length; ++i) {
0798: doDoc(docs[i]);
0799: }
0800: }
0801: }
0802:
0803: private void doDoc(ProgramElementDoc doc) {
0804: if (ignore(doc))
0805: return;
0806:
0807: if (doc.isClass() || doc.isInterface()) {
0808: ClassDoc cdoc = (ClassDoc) doc;
0809: doDocs(cdoc.fields());
0810: doDocs(cdoc.constructors());
0811: doDocs(cdoc.methods());
0812: doDocs(cdoc.innerClasses());
0813: }
0814:
0815: Info info = new Info();
0816: if (info.read(doc)) {
0817: results.add(info);
0818: }
0819: }
0820:
0821: private boolean ignore(ProgramElementDoc doc) {
0822: if (doc == null)
0823: return true;
0824: if (doc.isPrivate() || doc.isPackagePrivate())
0825: return true;
0826: if (doc instanceof ConstructorDoc
0827: && ((ConstructorDoc) doc).isSynthetic())
0828: return true;
0829: if (doc.qualifiedName().indexOf(".misc") != -1)
0830: return true;
0831: Tag[] tags = doc.tags();
0832: for (int i = 0; i < tags.length; ++i) {
0833: if (tagKindIndex(tags[i].kind()) == INTERNAL)
0834: return true;
0835: }
0836:
0837: return false;
0838: }
0839:
0840: private static void writeResults(Collection c, BufferedWriter w,
0841: boolean brief, boolean html, boolean detail) {
0842: Iterator iter = c.iterator();
0843: String pack = null;
0844: String clas = null;
0845: while (iter.hasNext()) {
0846: APIInfo info = (APIInfo) iter.next();
0847: if (brief) {
0848: info.write(w, brief, false, detail);
0849: } else {
0850: try {
0851: String p = info.get(PAK, true);
0852: if (!p.equals(pack)) {
0853: w.newLine();
0854: if (html) {
0855: if (clas != null) {
0856: w.write("</ul>");
0857: w.newLine();
0858: }
0859: if (pack != null) {
0860: w.write("</ul>");
0861: w.newLine();
0862: }
0863:
0864: w.write("<h3>Package ");
0865: w.write(p);
0866: w.write("</h3>");
0867: w.newLine();
0868: w.write("<ul>");
0869: w.newLine();
0870: } else {
0871: w.write("Package ");
0872: w.write(p);
0873: w.write(':');
0874: }
0875: w.newLine();
0876: w.newLine();
0877:
0878: pack = p;
0879: clas = null;
0880: }
0881:
0882: if (info.getVal(CAT) != CAT_CLASS) {
0883: String name = info.get(CLS, true);
0884: if (!name.equals(clas)) {
0885: if (html) {
0886: if (clas != null) {
0887: w.write("</ul>");
0888: }
0889: w.write("<li>");
0890: w.write(name);
0891: w.newLine();
0892: w.write("<ul>");
0893: } else {
0894: w.write(name);
0895: w.newLine();
0896: }
0897: clas = name;
0898: }
0899: w.write(" ");
0900: }
0901: if (html) {
0902: w.write("<li>");
0903: info.write(w, brief, html, detail);
0904: w.write("</li>");
0905: } else {
0906: info.write(w, brief, html, detail);
0907: }
0908: } catch (IOException e) {
0909: System.err.println("IOException " + e.getMessage()
0910: + " writing " + info);
0911: }
0912: }
0913: }
0914: if (html) {
0915: try {
0916: if (clas != null) {
0917: w.write("</ul>");
0918: w.newLine();
0919: }
0920: if (pack != null) {
0921: w.write("</ul>");
0922: w.newLine();
0923: }
0924: } catch (IOException e) {
0925: }
0926: }
0927: }
0928:
0929: private static String readToken(BufferedReader r)
0930: throws IOException {
0931: char[] buf = new char[256];
0932: int i = 0;
0933: for (; i < buf.length; ++i) {
0934: int c = r.read();
0935: if (c == -1) {
0936: throw new IOException("unexpected EOF");
0937: } else if (c == SEP) {
0938: break;
0939: }
0940: buf[i] = (char) c;
0941: }
0942: if (i == buf.length) {
0943: throw new IOException("unterminated token"
0944: + new String(buf));
0945: }
0946:
0947: return new String(buf, 0, i);
0948: }
0949:
0950: private static TreeSet stripAndResort(TreeSet t) {
0951: stripClassInfo(t);
0952: TreeSet r = new TreeSet(Info.classFirstComparator());
0953: r.addAll(t);
0954: return r;
0955: }
0956:
0957: private static void stripClassInfo(Collection c) {
0958: // c is sorted with class info first
0959: Iterator iter = c.iterator();
0960: String cname = null;
0961: while (iter.hasNext()) {
0962: Info info = (Info) iter.next();
0963: String cls = info.get(CLS, true);
0964: if (cname != null) {
0965: if (cname.equals(cls)) {
0966: iter.remove();
0967: continue;
0968: }
0969: cname = null;
0970: }
0971: if (info.getVal(CAT) == CAT_CLASS) {
0972: cname = info.get(NAM, true);
0973: }
0974: }
0975: }
0976:
0977: private static int tagStatus(final Doc doc) {
0978: class Result {
0979: int res = -1;
0980:
0981: void set(int val) {
0982: if (res != -1)
0983: throw new RuntimeException("bad doc: " + doc);
0984: res = val;
0985: }
0986:
0987: int get() {
0988: if (res == -1) {
0989: System.err.println("warning: no tag for " + doc);
0990: return 0;
0991: }
0992: return res;
0993: }
0994: }
0995:
0996: Tag[] tags = doc.tags();
0997: Result result = new Result();
0998: for (int i = 0; i < tags.length; ++i) {
0999: Tag tag = tags[i];
1000:
1001: String kind = tag.kind();
1002: int ix = tagKindIndex(kind);
1003:
1004: switch (ix) {
1005: case INTERNAL:
1006: result.set(-2);
1007: break;
1008:
1009: case DRAFT:
1010: result.set(STA_DRAFT);
1011: break;
1012:
1013: case STABLE:
1014: result.set(STA_STABLE);
1015: break;
1016:
1017: case DEPRECATED:
1018: result.set(STA_DEPRECATED);
1019: break;
1020:
1021: case OBSOLETE:
1022: result.set(STA_OBSOLETE);
1023: break;
1024:
1025: case SINCE:
1026: case EXCEPTION:
1027: case VERSION:
1028: case UNKNOWN:
1029: case AUTHOR:
1030: case SEE:
1031: case PARAM:
1032: case RETURN:
1033: case THROWS:
1034: case SERIAL:
1035: break;
1036:
1037: default:
1038: throw new RuntimeException("unknown index " + ix
1039: + " for tag: " + kind);
1040: }
1041: }
1042:
1043: return result.get();
1044: }
1045:
1046: private static final int UNKNOWN = -1;
1047: private static final int INTERNAL = 0;
1048: private static final int DRAFT = 1;
1049: private static final int STABLE = 2;
1050: private static final int SINCE = 3;
1051: private static final int DEPRECATED = 4;
1052: private static final int AUTHOR = 5;
1053: private static final int SEE = 6;
1054: private static final int VERSION = 7;
1055: private static final int PARAM = 8;
1056: private static final int RETURN = 9;
1057: private static final int THROWS = 10;
1058: private static final int OBSOLETE = 11;
1059: private static final int EXCEPTION = 12;
1060: private static final int SERIAL = 13;
1061:
1062: private static int tagKindIndex(String kind) {
1063: final String[] tagKinds = { "@internal", "@draft", "@stable",
1064: "@since", "@deprecated", "@author", "@see", "@version",
1065: "@param", "@return", "@throws", "@obsolete",
1066: "@exception", "@serial" };
1067:
1068: for (int i = 0; i < tagKinds.length; ++i) {
1069: if (kind.equals(tagKinds[i])) {
1070: return i;
1071: }
1072: }
1073: return UNKNOWN;
1074: }
1075: }
|