2 *******************************************************************************
\r
3 * Copyright (C) 2004-2008, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
9 * Generate a list of ICU's public APIs, sorted by qualified name and signature
\r
10 * public APIs are all non-internal, non-package apis in com.ibm.icu.[lang|math|text|util].
\r
11 * For each API, list
\r
12 * - public, package, protected, or private (PB PK PT PR)
\r
13 * - static or non-static (STK NST)
\r
14 * - final or non-final (FN NF)
\r
15 * - synchronized or non-synchronized (SYN NSY)
\r
16 * - stable, draft, deprecated, obsolete (ST DR DP OB)
\r
17 * - abstract or non-abstract (AB NA)
\r
18 * - constructor, member, field (C M F)
\r
20 * Requires JDK 1.4.2 or later
\r
22 * Sample compilation:
\r
23 * c:/doug/java/jdk1.4.2/build/windows-i586/bin/javac *.java
\r
26 * c:/j2sdk1.4.2/bin/javadoc
\r
27 * -classpath c:/jd2sk1.4.2/lib/tools.jar
\r
28 * -doclet com.ibm.icu.dev.tool.docs.GatherAPIData
\r
29 * -docletpath c:/doug/cvsproj/icu4j/src
\r
30 * -sourcepath c:/doug/cvsproj/icu4j/src
\r
32 * -output icu4j30.api
\r
35 * com.ibm.icu.lang com.ibm.icu.math com.ibm.icu.text com.ibm.icu.util
\r
37 * todo: provide command-line control of filters of which subclasses/packages to process
\r
38 * todo: record full inheritance heirarchy, not just immediate inheritance
\r
39 * todo: allow for aliasing comparisons (force (pkg.)*class to be treated as though it
\r
40 * were in a different pkg/class heirarchy (facilitates comparison of icu4j and java)
\r
43 package com.ibm.icu.dev.tool.docs;
\r
45 // standard release sdk won't work, need internal build to get access to javadoc
\r
46 import com.sun.javadoc.*;
\r
49 import java.util.regex.*;
\r
50 import java.util.zip.GZIPOutputStream;
\r
51 import java.util.zip.ZipEntry;
\r
52 import java.util.zip.ZipOutputStream;
\r
54 public class GatherAPIData {
\r
57 String srcName = "Current"; // default source name
\r
58 String output; // name of output file to write
\r
59 String base; // strip this prefix
\r
66 public static int optionLength(String option) {
\r
67 if (option.equals("-name")) {
\r
69 } else if (option.equals("-output")) {
\r
71 } else if (option.equals("-base")) {
\r
73 } else if (option.equals("-filter")) {
\r
75 } else if (option.equals("-zip")) {
\r
77 } else if (option.equals("-gzip")) {
\r
79 } else if (option.equals("-internal")) {
\r
81 } else if (option.equals("-version")) {
\r
87 public static boolean start(RootDoc root) {
\r
88 return new GatherAPIData(root).run();
\r
91 GatherAPIData(RootDoc root) {
\r
94 String[][] options = root.options();
\r
95 for (int i = 0; i < options.length; ++i) {
\r
96 String opt = options[i][0];
\r
97 if (opt.equals("-name")) {
\r
98 this.srcName = options[i][1];
\r
99 } else if (opt.equals("-output")) {
\r
100 this.output = options[i][1];
\r
101 } else if (opt.equals("-base")) {
\r
102 this.base = options[i][1]; // should not include '.'
\r
103 } else if (opt.equals("-filter")) {
\r
104 this.pat = Pattern.compile(options[i][1], Pattern.CASE_INSENSITIVE);
\r
105 } else if (opt.equals("-zip")) {
\r
107 } else if (opt.equals("-gzip")) {
\r
109 } else if (opt.equals("-internal")) {
\r
110 this.internal = true;
\r
111 } else if (opt.equals("-version")) {
\r
112 this.version = true;
\r
116 results = new TreeSet(APIInfo.defaultComparator());
\r
119 private boolean run() {
\r
120 doDocs(root.classes());
\r
122 OutputStream os = System.out;
\r
123 if (output != null) {
\r
126 ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(output + ".zip"));
\r
127 zos.putNextEntry(new ZipEntry(output));
\r
130 os = new GZIPOutputStream(new FileOutputStream(output + ".gz"));
\r
132 os = new FileOutputStream(output);
\r
135 catch (IOException e) {
\r
136 RuntimeException re = new RuntimeException(e.getMessage());
\r
142 BufferedWriter bw = null;
\r
144 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
\r
145 bw = new BufferedWriter(osw);
\r
147 // writing data file
\r
148 bw.write(String.valueOf(APIInfo.VERSION) + APIInfo.SEP); // header version
\r
149 bw.write(srcName + APIInfo.SEP); // source name
\r
150 bw.write((base == null ? "" : base) + APIInfo.SEP); // base
\r
152 writeResults(results, bw);
\r
153 bw.close(); // should flush, close all, etc
\r
154 } catch (IOException e) {
\r
155 try { bw.close(); } catch (IOException e2) {}
\r
156 RuntimeException re = new RuntimeException("write error: " + e.getMessage());
\r
164 private void doDocs(ProgramElementDoc[] docs) {
\r
165 if (docs != null && docs.length > 0) {
\r
166 for (int i = 0; i < docs.length; ++i) {
\r
172 private void doDoc(ProgramElementDoc doc) {
\r
173 if (ignore(doc)) return;
\r
175 if (doc.isClass() || doc.isInterface()) {
\r
176 ClassDoc cdoc = (ClassDoc)doc;
\r
177 doDocs(cdoc.fields());
\r
178 doDocs(cdoc.constructors());
\r
179 doDocs(cdoc.methods());
\r
180 doDocs(cdoc.innerClasses());
\r
183 APIInfo info = createInfo(doc);
\r
184 if (info != null) {
\r
189 private boolean ignore(ProgramElementDoc doc) {
\r
190 if (doc == null) return true;
\r
191 if (doc.isPrivate() || doc.isPackagePrivate()) return true;
\r
192 if (doc instanceof ConstructorDoc && ((ConstructorDoc)doc).isSynthetic()) return true;
\r
193 if (doc.qualifiedName().indexOf(".misc") != -1) {
\r
194 System.out.println("misc: " + doc.qualifiedName()); return true;
\r
196 if (!internal) { // debug
\r
197 Tag[] tags = doc.tags();
\r
198 for (int i = 0; i < tags.length; ++i) {
\r
199 if (tagKindIndex(tags[i].kind()) == INTERNAL) { return true; }
\r
202 if (pat != null && (doc.isClass() || doc.isInterface())) {
\r
203 if (!pat.matcher(doc.name()).matches()) {
\r
210 private static void writeResults(Collection c, BufferedWriter w) {
\r
211 Iterator iter = c.iterator();
\r
212 while (iter.hasNext()) {
\r
213 APIInfo info = (APIInfo)iter.next();
\r
218 private String trimBase(String arg) {
\r
219 if (base != null) {
\r
220 for (int n = arg.indexOf(base); n != -1; n = arg.indexOf(base, n)) {
\r
221 arg = arg.substring(0, n) + arg.substring(n+base.length());
\r
227 public APIInfo createInfo(ProgramElementDoc doc) {
\r
230 // Doc. isField, isMethod, isConstructor, isClass, isInterface
\r
231 // ProgramElementDoc. containingClass, containingPackage
\r
232 // ProgramElementDoc. isPublic, isProtected, isPrivate, isPackagePrivate
\r
233 // ProgramElementDoc. isStatic, isFinal
\r
234 // MemberDoc.isSynthetic
\r
235 // ExecutableMemberDoc isSynchronized, signature
\r
236 // Type.toString() // e.g. "String[][]"
\r
237 // ClassDoc.isAbstract, superClass, interfaces, fields, methods, constructors, innerClasses
\r
239 // ConstructorDoc qualifiedName
\r
240 // MethodDoc isAbstract, returnType
\r
242 APIInfo info = new APIInfo();
\r
244 info.includeStatusVersion(true);
\r
248 String[] version = new String[1];
\r
249 info.setType(APIInfo.STA, tagStatus(doc, version));
\r
250 info.setStatusVersion(version[0]);
\r
253 if (doc.isPublic()) {
\r
255 } else if (doc.isProtected()) {
\r
256 info.setProtected();
\r
257 } else if (doc.isPrivate()) {
\r
260 // default is package
\r
264 if (doc.isStatic()) {
\r
267 // default is non-static
\r
271 if (doc.isFinal()) {
\r
274 // default is non-final
\r
278 if (doc.isField()) {
\r
280 } else if (doc.isMethod()) {
\r
282 } else if (doc.isConstructor()) {
\r
283 info.setConstructor();
\r
284 } else if (doc.isClass() || doc.isInterface()) {
\r
288 info.setPackage(trimBase(doc.containingPackage().name()));
\r
289 info.setClassName((doc.isClass() || doc.isInterface() || (doc.containingClass() == null))
\r
291 : trimBase(doc.containingClass().name()));
\r
292 info.setName(trimBase(doc.name()));
\r
294 if (doc instanceof FieldDoc) {
\r
295 FieldDoc fdoc = (FieldDoc)doc;
\r
296 info.setSignature(trimBase(fdoc.type().toString()));
\r
297 } else if (doc instanceof ClassDoc) {
\r
298 ClassDoc cdoc = (ClassDoc)doc;
\r
300 if (cdoc.isClass() && cdoc.isAbstract()) {
\r
301 // interfaces are abstract by default, don't mark them as abstract
\r
302 info.setAbstract();
\r
305 StringBuffer buf = new StringBuffer();
\r
306 if (cdoc.isClass()) {
\r
307 buf.append("extends ");
\r
308 buf.append(cdoc.superclass().qualifiedName());
\r
310 ClassDoc[] imp = cdoc.interfaces();
\r
311 if (imp != null && imp.length > 0) {
\r
312 if (buf.length() > 0) {
\r
315 buf.append("implements");
\r
316 for (int i = 0; i < imp.length; ++i) {
\r
321 buf.append(imp[i].qualifiedName());
\r
324 info.setSignature(trimBase(buf.toString()));
\r
326 ExecutableMemberDoc emdoc = (ExecutableMemberDoc)doc;
\r
327 if (emdoc.isSynchronized()) {
\r
328 info.setSynchronized();
\r
331 if (doc instanceof MethodDoc) {
\r
332 MethodDoc mdoc = (MethodDoc)doc;
\r
333 if (mdoc.isAbstract()) {
\r
334 info.setAbstract();
\r
336 info.setSignature(trimBase(mdoc.returnType().toString() + emdoc.signature()));
\r
339 info.setSignature(trimBase(emdoc.signature()));
\r
346 private int tagStatus(final Doc doc, String[] version) {
\r
349 void set(int val) {
\r
351 if (val == APIInfo.STA_DEPRECATED) {
\r
352 // ok to have both a 'standard' tag and deprecated
\r
354 } else if (res != APIInfo.STA_DEPRECATED) {
\r
355 // if already not deprecated, this is an error
\r
356 System.err.println("bad doc: " + doc + " both: " + APIInfo.getTypeValName(APIInfo.STA, res) + " and: " + APIInfo.getTypeValName(APIInfo.STA, val));
\r
360 // ok to replace with new tag
\r
365 System.err.println("warning: no tag for " + doc);
\r
372 Tag[] tags = doc.tags();
\r
373 Result result = new Result();
\r
374 String statusVer = "";
\r
375 for (int i = 0; i < tags.length; ++i) {
\r
378 String kind = tag.kind();
\r
379 int ix = tagKindIndex(kind);
\r
383 result.set(internal ? APIInfo.STA_INTERNAL : -2); // -2 for legacy compatibility
\r
384 statusVer = getStatusVersion(tag);
\r
388 result.set(APIInfo.STA_DRAFT);
\r
389 statusVer = getStatusVersion(tag);
\r
393 result.set(APIInfo.STA_STABLE);
\r
394 statusVer = getStatusVersion(tag);
\r
398 result.set(APIInfo.STA_DEPRECATED);
\r
399 statusVer = getStatusVersion(tag);
\r
403 result.set(APIInfo.STA_OBSOLETE);
\r
404 statusVer = getStatusVersion(tag);
\r
420 throw new RuntimeException("unknown index " + ix + " for tag: " + kind);
\r
424 if (version != null) {
\r
425 version[0] = statusVer;
\r
427 return result.get();
\r
430 private String getStatusVersion(Tag tag) {
\r
431 String text = tag.text();
\r
432 if (text != null && text.length() > 0) {
\r
433 // Extract version string
\r
436 for (; i < text.length(); i++) {
\r
437 char ch = text.charAt(i);
\r
438 if (ch == '.' || (ch >= '0' && ch <= '9')) {
\r
442 } else if (start != -1) {
\r
447 return text.substring(start, i);
\r
453 private static final int UNKNOWN = -1;
\r
454 private static final int INTERNAL = 0;
\r
455 private static final int DRAFT = 1;
\r
456 private static final int STABLE = 2;
\r
457 private static final int SINCE = 3;
\r
458 private static final int DEPRECATED = 4;
\r
459 private static final int AUTHOR = 5;
\r
460 private static final int SEE = 6;
\r
461 private static final int VERSION = 7;
\r
462 private static final int PARAM = 8;
\r
463 private static final int RETURN = 9;
\r
464 private static final int THROWS = 10;
\r
465 private static final int OBSOLETE = 11;
\r
466 private static final int EXCEPTION = 12;
\r
467 private static final int SERIAL = 13;
\r
469 private static int tagKindIndex(String kind) {
\r
470 final String[] tagKinds = {
\r
471 "@internal", "@draft", "@stable", "@since", "@deprecated", "@author", "@see", "@version",
\r
472 "@param", "@return", "@throws", "@obsolete", "@exception", "@serial"
\r
475 for (int i = 0; i < tagKinds.length; ++i) {
\r
476 if (kind.equals(tagKinds[i])) {
\r