ICU-3743 split icu4j api data gathering from report generation

X-SVN-Rev: 15245
This commit is contained in:
Doug Felt 2004-05-11 01:27:36 +00:00
parent 350aaa2bf8
commit 6bd7decbd2
3 changed files with 1250 additions and 0 deletions

View file

@ -0,0 +1,463 @@
/**
*******************************************************************************
* Copyright (C) 2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
/**
* Represents the API information on a doc element.
*/
package com.ibm.icu.dev.tool.docs;
import java.io.*;
import java.util.*;
class APIInfo {
// version id for the format of the APIInfo data
public static final int VERSION = 1;
// public keys and values for queries on info
public static final int STA = 0, STA_DRAFT = 0, STA_STABLE = 1, STA_DEPRECATED = 2, STA_OBSOLETE = 3;
public static final int VIS = 1, VIS_PACKAGE = 0, VIS_PUBLIC= 1, VIS_PROTECTED = 2, VIS_PRIVATE = 3;
public static final int STK = 2, STK_STATIC = 1;
public static final int FIN = 3, FIN_FINAL = 1;
public static final int SYN = 4, SYN_SYNCHRONIZED = 1;
public static final int ABS = 5, ABS_ABSTRACT = 1;
public static final int CAT = 6, CAT_CLASS = 0, CAT_FIELD = 1, CAT_CONSTRUCTOR = 2, CAT_METHOD = 3;
public static final int PAK = 7;
public static final int CLS = 8;
public static final int NAM = 9;
public static final int SIG = 10;
public static final int EXC = 11;
public static final int NUM_TYPES = 11;
// the separator between tokens in the data file
public static final char SEP = ';';
// internal state
private int info; // information about numeric values packed into an int as 2-bit nibbles
private String pack; // package
private String cls; // enclosing class
private String name; // name
private String sig; // signature, class: inheritance, method: signature, field: type, const: signature
private String exc; // throws
public void setDraft() { setType(STA, STA_DRAFT); }
public void setStable() { setType(STA, STA_STABLE); }
public void setDeprecated() { setType(STA, STA_DEPRECATED); }
public void setObsolete() { setType(STA, STA_OBSOLETE); }
public void setPackage() { setType(VIS, VIS_PACKAGE); }
public void setPublic() { setType(VIS, VIS_PUBLIC); }
public void setProtected() { setType(VIS, VIS_PROTECTED); }
public void setPrivate() { setType(VIS, VIS_PRIVATE); }
public void setStatic() { setType(STK, STK_STATIC); }
public void setFinal() { setType(FIN, FIN_FINAL); }
public void setSynchronized() { setType(SYN, SYN_SYNCHRONIZED); }
public void setAbstract() { setType(ABS, ABS_ABSTRACT); }
public void setClass() { setType(CAT, CAT_CLASS); }
public void setField() { setType(CAT, CAT_FIELD); }
public void setConstructor() { setType(CAT, CAT_CONSTRUCTOR); }
public void setMethod() { setType(CAT, CAT_METHOD); }
public void setPackage(String val) { setType(PAK, val); }
public void setClassName(String val) { setType(CLS, val); }
public void setName(String val) { setType(NAM, val); }
public void setSignature(String val) { setType(SIG, val); }
public void setExceptions(String val) { setType(EXC, val); }
public boolean isDraft() { return getVal(STA) == STA_DRAFT; }
public boolean isStable() { return getVal(STA) == STA_STABLE; }
public boolean isDeprecated() { return getVal(STA) == STA_DEPRECATED; }
public boolean isObsolete() { return getVal(STA) == STA_OBSOLETE; }
public boolean isPackage() { return getVal(VIS) == VIS_PACKAGE; }
public boolean isPublic() { return getVal(VIS) == VIS_PUBLIC; }
public boolean isProtected() { return getVal(VIS) == VIS_PROTECTED; }
public boolean isPrivate() { return getVal(VIS) == VIS_PRIVATE; }
public boolean isStatic() { return getVal(STK) == STK_STATIC; }
public boolean isFinal() { return getVal(FIN) == FIN_FINAL; }
public boolean isSynchronized() { return getVal(SYN) == SYN_SYNCHRONIZED; }
public boolean isAbstract() { return getVal(ABS) == ABS_ABSTRACT; }
public boolean isClass() { return getVal(CAT) == CAT_CLASS; }
public boolean isField() { return getVal(CAT) == CAT_FIELD; }
public boolean isConstructor() { return getVal(CAT) == CAT_CONSTRUCTOR; }
public boolean isMethod() { return getVal(CAT) == CAT_METHOD; }
public String getPackageName() { return get(PAK, true); }
public String getClassName() { return get(CLS, true); }
public String getName() { return get(NAM, true); }
public String getSignature() { return get(SIG, true); }
public String getExceptions() { return get(EXC, true); }
/**
* Return the integer value for the provided type. The type
* must be one of the defined type names. The return value
* will be one of corresponding values for that type.
*/
public int getVal(int typ) {
validateType(typ);
return (info >> (typ*2)) & 0x3;
}
/**
* Return the string value for the provided type. The type
* must be one of the defined type names. The return value
* will be one of corresponding values for that type. Brief
* should be true for writing data files, false for presenting
* information to the user.
*/
public String get(int typ, boolean brief) {
validateType(typ);
String[] vals = brief ? shortNames[typ] : names[typ];
if (vals == null) {
switch (typ) {
case PAK: return pack;
case CLS: return cls;
case NAM: return name;
case SIG: return sig;
case EXC: return exc;
}
}
int val = (info >> (typ*2)) & 0x3;
return vals[val];
}
/**
* Set the numeric value for the type. The value should be a
* value corresponding to the type. Only the lower two bits
* of the value are used.
*/
public void setType(int typ, int val) {
validateType(typ);
info &= ~(0x3 << (typ*2));
info |= (val&0x3) << (typ * 2);
}
/**
* Set the string value for the type. For numeric types,
* the value should be a string in 'brief' format. For
* non-numeric types, the value can be any
* string.
*/
private void setType(int typ, String val) {
validateType(typ);
String[] vals = shortNames[typ];
if (vals == null) {
switch (typ) {
case PAK: pack = val; break;
case CLS: cls = val; break;
case NAM: name = val; break;
case SIG: sig = val; break;
case EXC: exc = val; break;
}
return;
}
for (int i = 0; i < vals.length; ++i) {
if (val.equalsIgnoreCase(vals[i])) {
info &= ~(0x3 << (typ*2));
info |= i << (typ*2);
return;
}
}
throw new IllegalArgumentException("unrecognized value '" + val + "' for type '" + typeNames[typ] + "'");
}
/**
* Write the information out as a single line in brief format.
* If there are IO errors, throws a RuntimeException.
*/
public void writeln(BufferedWriter w) {
try {
for (int i = 0; i < NUM_TYPES; ++i) {
String s = get(i, true);
if (s != null) {
w.write(s);
}
w.write(SEP);
}
w.newLine();
}
catch (IOException e) {
RuntimeException re = new RuntimeException("IO Error");
re.initCause(e);
throw re;
}
}
/**
* Read a record from the input and initialize this APIInfo.
* Return true if successfule, false if EOF, otherwise throw
* a RuntimeException.
*/
public boolean read(BufferedReader r) {
int i = 0;
try {
for (; i < NUM_TYPES; ++i) {
setType(i, readToken(r));
}
r.readLine(); // swallow line end sequence
}
catch (IOException e) {
if (i == 0) { // assume if first read returns error, we have reached end of input
return false;
}
RuntimeException re = new RuntimeException("IO Error");
re.initCause(e);
throw re;
}
return true;
}
/**
* Read one token from input, which should have been written by
* APIInfo. Throws IOException if EOF is encountered before the
* token is complete (i.e. before the separator character is
* encountered) or if the token exceeds the maximum length of
* 255 chars.
*/
public static String readToken(BufferedReader r) throws IOException {
char[] buf = new char[256];
int i = 0;
for (; i < buf.length; ++i) {
int c = r.read();
if (c == -1) {
throw new IOException("unexpected EOF");
} else if (c == SEP) {
break;
}
buf[i] = (char)c;
}
if (i == buf.length) {
throw new IOException("unterminated token" + new String(buf));
}
return new String(buf, 0, i);
}
/**
* The default comparator for APIInfo. This compares packages, class/name
* (as the info represents a class or other object), category, name,
* and signature.
*/
public static Comparator defaultComparator() {
final Comparator c = new Comparator() {
public int compare(Object lhs, Object rhs) {
APIInfo lhi = (APIInfo)lhs;
APIInfo rhi = (APIInfo)rhs;
int result = lhi.pack.compareTo(rhi.pack);
if (result == 0) {
result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name : lhi.cls)
.compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name : rhi.cls);
if (result == 0) {
result = lhi.getVal(CAT)- rhi.getVal(CAT);
if (result == 0) {
result = lhi.name.compareTo(rhi.name);
if (result == 0) {
result = lhi.sig.compareTo(rhi.sig);
}
}
}
}
return result;
}
};
return c;
}
/**
* This compares two APIInfos by package, class/name, category, name, and then if
* the APIInfo does not represent a class, by signature. The difference between
* this and the default comparator is that APIInfos representing classes are considered
* equal regardless of their signatures (which represent inheritance for classes).
*/
public static Comparator changedComparator() {
final Comparator c = new Comparator() {
public int compare(Object lhs, Object rhs) {
APIInfo lhi = (APIInfo)lhs;
APIInfo rhi = (APIInfo)rhs;
int result = lhi.pack.compareTo(rhi.pack);
if (result == 0) {
result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name : lhi.cls)
.compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name : rhi.cls);
if (result == 0) {
result = lhi.getVal(CAT)- rhi.getVal(CAT);
if (result == 0) {
result = lhi.name.compareTo(rhi.name);
if (result == 0 && lhi.getVal(CAT) != CAT_CLASS) {
result = lhi.sig.compareTo(rhi.sig);
}
}
}
}
return result;
}
};
return c;
}
/**
* This compares two APIInfos by package, then sorts classes before non-classes, then
* by class/name, category, name, and signature.
*/
public static Comparator classFirstComparator() {
final Comparator c = new Comparator() {
public int compare(Object lhs, Object rhs) {
APIInfo lhi = (APIInfo)lhs;
APIInfo rhi = (APIInfo)rhs;
int result = lhi.pack.compareTo(rhi.pack);
if (result == 0) {
boolean lcls = lhi.getVal(CAT) == CAT_CLASS;
boolean rcls = rhi.getVal(CAT) == CAT_CLASS;
result = lcls == rcls ? 0 : (lcls ? -1 : 1);
if (result == 0) {
result = (lcls ? lhi.name : lhi.cls).compareTo(rcls ? rhi.name : rhi.cls);
if (result == 0) {
result = lhi.getVal(CAT)- rhi.getVal(CAT);
if (result == 0) {
result = lhi.name.compareTo(rhi.name);
if (result == 0 && !lcls) {
result = lhi.sig.compareTo(rhi.sig);
}
}
}
}
}
return result;
}
};
return c;
}
/**
* Write the data in report format.
*/
public void print(PrintWriter pw, boolean detail) {
StringBuffer buf = new StringBuffer();
// remove all occurrences of icu packages from the param string
// fortunately, all the packages have 4 chars (lang, math, text, util).
String xsig = sig;
if (!detail) {
final String ICUPACK = "com.ibm.icu.";
StringBuffer tbuf = new StringBuffer();
for (int i = 0; i < sig.length();) {
int n = sig.indexOf(ICUPACK, i);
if (n == -1) {
tbuf.append(sig.substring(i));
break;
}
tbuf.append(sig.substring(i, n));
i = n + ICUPACK.length() + 5; // trailing 'xxxx.'
}
xsig = tbuf.toString();
}
// construct signature
for (int i = STA; i < CAT; ++i) { // include status
String s = get(i, false);
if (s != null && s.length() > 0) {
buf.append(s);
buf.append(' ');
}
}
int val = getVal(CAT);
switch (val) {
case CAT_CLASS:
if (sig.indexOf("extends") == -1) {
buf.append("interface ");
} else {
buf.append("class ");
}
if (cls.length() > 0) {
buf.append(cls);
buf.append('.');
}
buf.append(name);
if (detail) {
buf.append(' ');
buf.append(sig);
}
break;
case CAT_FIELD:
buf.append(xsig);
buf.append(' ');
buf.append(name);
break;
case CAT_METHOD:
case CAT_CONSTRUCTOR:
int n = xsig.indexOf('(');
if (n > 0) {
buf.append(xsig.substring(0, n));
buf.append(' ');
} else {
n = 0;
}
buf.append(name);
buf.append(xsig.substring(n));
break;
}
pw.print(buf.toString());
}
public void println(PrintWriter pw, boolean detail) {
print(pw, detail);
pw.println();
}
private static final String[] typeNames = {
"status", "visibility", "static", "final", "synchronized",
"abstract", "category", "package", "class", "name", "signature"
};
private static final String[][] names = {
{ "(draft) ", "(stable) ", "(deprecated)", "(obsolete) " },
{ "package", "public", "protected", "private" },
{ "", "static" },
{ "", "final" },
{ "", "synchronized" },
{ "", "abstract" },
{ "class", "field", "constructor", "method" },
null,
null,
null,
null,
null
};
private static final String[][] shortNames = {
{ "DR", "ST", "DP", "OB" },
{ "PK", "PB", "PT", "PR" },
{ "NS", "ST" },
{ "NF", "FN" },
{ "NS", "SY" },
{ "NA", "AB" },
{ "L", "F", "C", "M" },
null,
null,
null,
null,
null
};
private static void validateType(int typ) {
if (typ < 0 || typ > NUM_TYPES) {
throw new IllegalArgumentException("bad type index: " + typ);
}
}
public String toString() {
return get(NAM, true);
}
}

View file

@ -0,0 +1,408 @@
/**
*******************************************************************************
* Copyright (C) 2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
/**
* Generate a list of ICU's public APIs, sorted by qualified name and signature
* public APIs are all non-internal, non-package apis in com.ibm.icu.[lang|math|text|util].
* For each API, list
* - public, package, protected, or private (PB PK PT PR)
* - static or non-static (STK NST)
* - final or non-final (FN NF)
* - synchronized or non-synchronized (SYN NSY)
* - stable, draft, deprecated, obsolete (ST DR DP OB)
* - abstract or non-abstract (AB NA)
* - constructor, member, field (C M F)
*
* Requires JDK 1.4.2 or later
*
* Sample compilation:
* c:/doug/java/jdk1.4.2/build/windows-i586/bin/javac *.java
*
* Sample execution
* c:/j2sdk1.4.2/bin/javadoc
* -classpath c:/jd2sk1.4.2/lib/tools.jar
* -doclet com.ibm.icu.dev.tool.docs.GatherAPIData
* -docletpath c:/doug/cvsproj/icu4j/src
* -sourcepath c:/doug/cvsproj/icu4j/src
* -name "ICU4J 3.0"
* -output icu4j30.api
* -zip
* com.ibm.icu.text
*
* todo: separate generation of data files (which requires taglet) from
* comparison and report generation (which does not require it)
* todo: provide command-line control of filters of which subclasses/packages to process
* todo: record full inheritance heirarchy, not just immediate inheritance
* todo: allow for aliasing comparisons (force (pkg.)*class to be treated as though it
* were in a different pkg/class heirarchy (facilitates comparison of icu4j and java)
*/
package com.ibm.icu.dev.tool.docs;
// standard release sdk won't work, need internal build to get access to javadoc
import com.sun.javadoc.*;
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class GatherAPIData {
RootDoc root;
TreeSet results;
String srcName = "Current"; // default source name
String output; // name of output file to write
String base; // strip this prefix
String filter; // filter classes by name
boolean zip;
public static int optionLength(String option) {
if (option.equals("-name")) {
return 2;
} else if (option.equals("-output")) {
return 2;
} else if (option.equals("-base")) {
return 2;
} else if (option.equals("-filter")) {
return 2;
} else if (option.equals("-zip")) {
return 1;
}
return 0;
}
public static boolean start(RootDoc root) {
return new GatherAPIData(root).run();
}
GatherAPIData(RootDoc root) {
this.root = root;
String[][] options = root.options();
for (int i = 0; i < options.length; ++i) {
String opt = options[i][0];
if (opt.equals("-name")) {
this.srcName = options[i][1];
} else if (opt.equals("-output")) {
this.output = options[i][1];
} else if (opt.equals("-base")) {
this.base = options[i][1]; // should not include '.'
} else if (opt.equals("-filter")) {
this.filter = options[i][1];
} else if (opt.equals("-zip")) {
this.zip = true;
}
}
results = new TreeSet(APIInfo.defaultComparator());
}
private boolean run() {
doDocs(root.classes());
OutputStream os = System.out;
if (output != null) {
try {
if (zip) {
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(output + ".zip"));
zos.putNextEntry(new ZipEntry(output));
os = zos;
} else {
os = new FileOutputStream(output);
}
}
catch (IOException e) {
RuntimeException re = new RuntimeException(e.getMessage());
re.initCause(e);
throw re;
}
}
BufferedWriter bw = null;
try {
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
bw = new BufferedWriter(osw);
// writing data file
bw.write(String.valueOf(APIInfo.VERSION) + APIInfo.SEP); // header version
bw.write(srcName + APIInfo.SEP); // source name
bw.write((base == null ? "" : base) + APIInfo.SEP); // base
bw.newLine();
writeResults(results, bw);
bw.close(); // should flush, close all, etc
// if (zip) {
// ((ZipOutputStream)os).finish();
// }
} catch (IOException e) {
try { bw.close(); } catch (IOException e2) {}
RuntimeException re = new RuntimeException("write error: " + e.getMessage());
re.initCause(e);
throw re;
}
return false;
}
private void doDocs(ProgramElementDoc[] docs) {
if (docs != null && docs.length > 0) {
for (int i = 0; i < docs.length; ++i) {
doDoc(docs[i]);
}
}
}
private void doDoc(ProgramElementDoc doc) {
if (ignore(doc)) return;
if (doc.isClass() || doc.isInterface()) {
ClassDoc cdoc = (ClassDoc)doc;
doDocs(cdoc.fields());
doDocs(cdoc.constructors());
doDocs(cdoc.methods());
doDocs(cdoc.innerClasses());
}
APIInfo info = createInfo(doc);
if (info != null) {
results.add(info);
}
}
private boolean ignore(ProgramElementDoc doc) {
if (doc == null) return true;
if (doc.isPrivate() || doc.isPackagePrivate()) return true;
if (doc instanceof ConstructorDoc && ((ConstructorDoc)doc).isSynthetic()) return true;
if (doc.qualifiedName().indexOf(".misc") != -1) { System.out.println("misc: " + doc.qualifiedName()); return true; }
Tag[] tags = doc.tags();
for (int i = 0; i < tags.length; ++i) {
if (tagKindIndex(tags[i].kind()) == INTERNAL) return true;
}
return false;
}
private static void writeResults(Collection c, BufferedWriter w) {
Iterator iter = c.iterator();
while (iter.hasNext()) {
APIInfo info = (APIInfo)iter.next();
info.writeln(w);
}
}
private String trimBase(String arg) {
if (base != null) {
for (int n = arg.indexOf(base); n != -1; n = arg.indexOf(base, n)) {
arg = arg.substring(0, n) + arg.substring(n+base.length());
}
}
return arg;
}
public APIInfo createInfo(ProgramElementDoc doc) {
// Doc. name
// Doc. isField, isMethod, isConstructor, isClass, isInterface
// ProgramElementDoc. containingClass, containingPackage
// ProgramElementDoc. isPublic, isProtected, isPrivate, isPackagePrivate
// ProgramElementDoc. isStatic, isFinal
// MemberDoc.isSynthetic
// ExecutableMemberDoc isSynchronized, signature
// Type.toString() // e.g. "String[][]"
// ClassDoc.isAbstract, superClass, interfaces, fields, methods, constructors, innerClasses
// FieldDoc type
// ConstructorDoc qualifiedName
// MethodDoc isAbstract, returnType
APIInfo info = new APIInfo();
// status
info.setType(APIInfo.STA, tagStatus(doc));
// visibility
if (doc.isPublic()) {
info.setPublic();
} else if (doc.isProtected()) {
info.setProtected();
} else if (doc.isPrivate()) {
info.setPrivate();
} else {
// default is package
}
// static
if (doc.isStatic()) {
info.setStatic();
} else {
// default is non-static
}
// final
if (doc.isFinal()) {
info.setFinal();
} else {
// default is non-final
}
// type
if (doc.isField()) {
info.setField();
} else if (doc.isMethod()) {
info.setMethod();
} else if (doc.isConstructor()) {
info.setConstructor();
} else if (doc.isClass() || doc.isInterface()) {
info.setClass();
}
info.setPackage(trimBase(doc.containingPackage().name()));
info.setClassName((doc.isClass() || doc.isInterface() || (doc.containingClass() == null))
? ""
: trimBase(doc.containingClass().name()));
info.setName(trimBase(doc.name()));
if (doc instanceof FieldDoc) {
FieldDoc fdoc = (FieldDoc)doc;
info.setSignature(trimBase(fdoc.type().toString()));
} else if (doc instanceof ClassDoc) {
ClassDoc cdoc = (ClassDoc)doc;
if (cdoc.isClass() && cdoc.isAbstract()) { // interfaces are abstract by default, don't mark them as abstract
info.setAbstract();
}
StringBuffer buf = new StringBuffer();
if (cdoc.isClass()) {
buf.append("extends ");
buf.append(cdoc.superclass().qualifiedName());
}
ClassDoc[] imp = cdoc.interfaces();
if (imp != null && imp.length > 0) {
if (buf.length() > 0) {
buf.append(" ");
}
buf.append("implements");
for (int i = 0; i < imp.length; ++i) {
if (i != 0) {
buf.append(",");
}
buf.append(" ");
buf.append(imp[i].qualifiedName());
}
}
info.setSignature(trimBase(buf.toString()));
} else {
ExecutableMemberDoc emdoc = (ExecutableMemberDoc)doc;
if (emdoc.isSynchronized()) {
info.setSynchronized();
}
if (doc instanceof MethodDoc) {
MethodDoc mdoc = (MethodDoc)doc;
if (mdoc.isAbstract()) {
info.setAbstract();
}
info.setSignature(trimBase(mdoc.returnType().toString() + emdoc.signature()));
} else {
// constructor
info.setSignature(trimBase(emdoc.signature()));
}
}
return info;
}
private static int tagStatus(final Doc doc) {
class Result {
int res = -1;
void set(int val) { if (res != -1) throw new RuntimeException("bad doc: " + doc); res = val; }
int get() {
if (res == -1) {
System.err.println("warning: no tag for " + doc);
return 0;
}
return res;
}
};
Tag[] tags = doc.tags();
Result result = new Result();
for (int i = 0; i < tags.length; ++i) {
Tag tag = tags[i];
String kind = tag.kind();
int ix = tagKindIndex(kind);
switch (ix) {
case INTERNAL:
result.set(-2);
break;
case DRAFT:
result.set(APIInfo.STA_DRAFT);
break;
case STABLE:
result.set(APIInfo.STA_STABLE);
break;
case DEPRECATED:
result.set(APIInfo.STA_DEPRECATED);
break;
case OBSOLETE:
result.set(APIInfo.STA_OBSOLETE);
break;
case SINCE:
case EXCEPTION:
case VERSION:
case UNKNOWN:
case AUTHOR:
case SEE:
case PARAM:
case RETURN:
case THROWS:
case SERIAL:
break;
default:
throw new RuntimeException("unknown index " + ix + " for tag: " + kind);
}
}
return result.get();
}
private static final int UNKNOWN = -1;
private static final int INTERNAL = 0;
private static final int DRAFT = 1;
private static final int STABLE = 2;
private static final int SINCE = 3;
private static final int DEPRECATED = 4;
private static final int AUTHOR = 5;
private static final int SEE = 6;
private static final int VERSION = 7;
private static final int PARAM = 8;
private static final int RETURN = 9;
private static final int THROWS = 10;
private static final int OBSOLETE = 11;
private static final int EXCEPTION = 12;
private static final int SERIAL = 13;
private static int tagKindIndex(String kind) {
final String[] tagKinds = {
"@internal", "@draft", "@stable", "@since", "@deprecated", "@author", "@see", "@version",
"@param", "@return", "@throws", "@obsolete", "@exception", "@serial"
};
for (int i = 0; i < tagKinds.length; ++i) {
if (kind.equals(tagKinds[i])) {
return i;
}
}
return UNKNOWN;
}
}

View file

@ -0,0 +1,379 @@
/**
*******************************************************************************
* Copyright (C) 2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
/**
* Compare two API files (generated by GatherAPIData) and generate a report
* on the differences.
*
* Sample invocation:
* java -old: icu4j28.api.zip -new: icu4j30.api -html -out: icu4j_compare_28_30.html
*/
package com.ibm.icu.dev.tool.docs;
import com.sun.javadoc.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
public class ReportAPI {
APIData oldData;
APIData newData;
boolean html;
String outputFile;
TreeSet added;
TreeSet removed;
ArrayList changed;
static final class APIData {
private int version;
private String name;
private String base;
private TreeSet set;
static APIData read(BufferedReader br) {
try {
APIData data = new APIData();
data.version = Integer.parseInt(APIInfo.readToken(br)); // version
data.name = APIInfo.readToken(br);
data.base = APIInfo.readToken(br); // base
br.readLine();
data.set = new TreeSet(APIInfo.defaultComparator());
for (APIInfo info = new APIInfo(); info.read(br); info = new APIInfo()) {
data.set.add(info);
}
return data;
}
catch (IOException e) {
RuntimeException re = new RuntimeException("error reading api data");
re.initCause(e);
throw re;
}
}
static APIData read(String fileName) {
try {
InputStream is;
if (fileName.endsWith(".zip")) {
ZipFile zf = new ZipFile(fileName);
Enumeration entryEnum = zf.entries();
if (entryEnum.hasMoreElements()) {
ZipEntry entry = (ZipEntry)entryEnum.nextElement();
is = zf.getInputStream(entry);
// we only handle one!!!
} else {
throw new IOException("zip file is empty");
}
} else {
File f = new File(fileName);
is = new FileInputStream(f);
}
InputStreamReader isr = new InputStreamReader(is);
return read(new BufferedReader(isr));
}
catch (IOException e) {
RuntimeException re = new RuntimeException("error getting info stream: " + fileName);
re.initCause(e);
throw re;
}
}
}
static final class DeltaInfo extends APIInfo {
private APIInfo added;
private APIInfo removed;
DeltaInfo(APIInfo added, APIInfo removed) {
this.added = added;
this.removed = removed;
}
public int getVal(int typ) {
return added.getVal(typ);
}
public String get(int typ, boolean brief) {
return added.get(typ, brief);
}
}
public static void main(String[] args) {
String oldFile = null;
String newFile = null;
String outFile = null;
boolean html = false;
for (int i = 0; i < args.length; ++i) {
String arg = args[i];
if (arg.equals("-old:")) {
oldFile = args[++i];
} else if (arg.equals("-new:")) {
newFile = args[++i];
} else if (arg.equals("-out:")) {
outFile = args[++i];
} else if (arg.equals("-html")) {
html = true;
}
}
new ReportAPI(oldFile, newFile).writeReport(outFile, html);
}
ReportAPI(String oldFile, String newFile) {
oldData = APIData.read(oldFile);
newData = APIData.read(newFile);
// writing comparison info
removed = (TreeSet)oldData.set.clone();
removed.removeAll(newData.set);
added = (TreeSet)newData.set.clone();
added.removeAll(oldData.set);
changed = new ArrayList();
Iterator ai = added.iterator();
Iterator ri = removed.iterator();
Comparator c = APIInfo.changedComparator();
APIInfo a = null, r = null;
while (ai.hasNext() && ri.hasNext()) {
if (a == null) a = (APIInfo)ai.next();
if (r == null) r = (APIInfo)ri.next();
int result = c.compare(a, r);
if (result < 0) {
a = null;
} else if (result > 0) {
r = null;
} else {
changed.add(new DeltaInfo(a, r));
a = null; ai.remove();
r = null; ri.remove();
}
}
added = stripAndResort(added);
removed = stripAndResort(removed);
}
private boolean writeReport(String outFile, boolean html) {
OutputStream os = System.out;
if (outFile != null) {
try {
os = new FileOutputStream(outFile);
}
catch (FileNotFoundException e) {
RuntimeException re = new RuntimeException(e.getMessage());
re.initCause(e);
throw re;
}
}
PrintWriter pw = null;
try {
pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os, "UTF-8")));
}
catch (UnsupportedEncodingException e) {
throw new InternalError(); // UTF-8 should always be supported
}
String title = "ICU4J API Comparison: " + oldData.name + " with " + newData.name;
String info = "Contents generated by ReportAPI tool.";
String copyright = "Copyright (C) 2004, International Business Machines Corporation, All Rights Reserved.";
if (html) {
pw.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
pw.println("<html>");
pw.println("<head>");
pw.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
pw.println("<title>" + title + "</title>");
pw.println("</head>");
pw.println("<body>");
pw.println("<h1>" + title + "</h1>");
pw.println();
pw.println("<hr/>");
pw.println("<h2>Removed from " + oldData.name +"</h2>");
if (removed.size() > 0) {
printResults(removed, pw, true, false);
} else {
pw.println("<p>(no API removed)</p>");
}
pw.println();
pw.println("<hr/>");
pw.println("<h2>Changed in " + newData.name + " (old, new)</h2>");
if (changed.size() > 0) {
printResults(changed, pw, true, true);
} else {
pw.println("<p>(no API changed)</p>");
}
pw.println();
pw.println("<hr/>");
pw.println("<h2>Added in " + newData.name + "</h2>");
if (added.size() > 0) {
printResults(added, pw, true, false);
} else {
pw.println("<p>(no API added)</p>");
}
pw.println("<hr/>");
pw.println("<p><i>" + info + "<br/>" + copyright + "</i></p>");
pw.println("</body>");
pw.println("</html>");
} else {
pw.println(title);
pw.println();
pw.println();
pw.println("=== Removed from " + oldData.name + " ===");
if (removed.size() > 0) {
printResults(removed, pw, false, false);
} else {
pw.println("(no API removed)");
}
pw.println();
pw.println();
pw.println("=== Changed in " + newData.name + " (old, new) ===");
if (changed.size() > 0) {
printResults(changed, pw, false, true);
} else {
pw.println("(no API changed)");
}
pw.println();
pw.println();
pw.println("=== Added in " + newData.name + " ===");
if (added.size() > 0) {
printResults(added, pw, false, false);
} else {
pw.println("(no API added)");
}
pw.println();
pw.println("================");
pw.println(info);
pw.println(copyright);
}
pw.close();
return false;
}
private static void printResults(Collection c, PrintWriter pw, boolean html, boolean isChangedAPIs) {
Iterator iter = c.iterator();
String pack = null;
String clas = null;
while (iter.hasNext()) {
APIInfo info = (APIInfo)iter.next();
String packageName = info.getPackageName();
if (!packageName.equals(pack)) {
if (html) {
if (clas != null) {
pw.println("</ul>");
}
if (pack != null) {
pw.println("</ul>");
}
pw.println();
pw.println("<h3>Package " + packageName + "</h3>");
pw.print("<ul>");
} else {
if (pack != null) {
pw.println();
}
pw.println();
pw.println("Package " + packageName + ":");
}
pw.println();
pack = packageName;
clas = null;
}
if (!info.isClass()) {
String className = info.getClassName();
if (!className.equals(clas)) {
if (html) {
if (clas != null) {
pw.println("</ul>");
}
pw.println("<li>" + className);
pw.println("<ul>");
} else {
pw.println(className);
}
clas = className;
}
pw.print(" ");
}
if (html) {
pw.print("<li>");
if (info instanceof DeltaInfo) {
DeltaInfo dinfo = (DeltaInfo)info;
dinfo.removed.print(pw, isChangedAPIs);
pw.println("</br>");
dinfo.added.print(pw, isChangedAPIs);
} else {
info.print(pw, isChangedAPIs);
}
pw.println("</li>");
} else {
if (info instanceof DeltaInfo) {
DeltaInfo dinfo = (DeltaInfo)info;
dinfo.removed.println(pw, isChangedAPIs);
dinfo.added.println(pw, isChangedAPIs);
} else {
info.println(pw, isChangedAPIs);
}
}
}
if (html) {
if (clas != null) {
pw.println("</ul>");
}
if (pack != null) {
pw.println("</ul>");
}
}
pw.println();
}
private static TreeSet stripAndResort(TreeSet t) {
stripClassInfo(t);
TreeSet r = new TreeSet(APIInfo.classFirstComparator());
r.addAll(t);
return r;
}
private static void stripClassInfo(Collection c) {
// c is sorted with class info first
Iterator iter = c.iterator();
String cname = null;
while (iter.hasNext()) {
APIInfo info = (APIInfo)iter.next();
String className = info.getClassName();
if (cname != null) {
if (cname.equals(className)) {
iter.remove();
continue;
}
cname = null;
}
if (info.isClass()) {
cname = info.getName();
}
}
}
}