2 *******************************************************************************
3 * Copyright (C) 2004-2012, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
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].
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)
20 * Requires JDK 1.4.2 or later
23 * c:/doug/java/jdk1.4.2/build/windows-i586/bin/javac *.java
26 * c:/j2sdk1.4.2/bin/javadoc
27 * -classpath c:/jd2sk1.4.2/lib/tools.jar
28 * -doclet com.ibm.icu.dev.tool.docs.GatherAPIData
29 * -docletpath c:/doug/cvsproj/icu4j/src
30 * -sourcepath c:/doug/cvsproj/icu4j/src
35 * com.ibm.icu.lang com.ibm.icu.math com.ibm.icu.text com.ibm.icu.util
37 * todo: provide command-line control of filters of which subclasses/packages to process
38 * todo: record full inheritance heirarchy, not just immediate inheritance
39 * todo: allow for aliasing comparisons (force (pkg.)*class to be treated as though it
40 * were in a different pkg/class heirarchy (facilitates comparison of icu4j and java)
43 package com.ibm.icu.dev.tool.docs;
45 // standard release sdk won't work, need internal build to get access to javadoc
46 import java.io.BufferedWriter;
47 import java.io.FileOutputStream;
48 import java.io.IOException;
49 import java.io.OutputStream;
50 import java.io.OutputStreamWriter;
51 import java.util.Collection;
52 import java.util.Iterator;
53 import java.util.TreeSet;
54 import java.util.regex.Pattern;
55 import java.util.zip.GZIPOutputStream;
56 import java.util.zip.ZipEntry;
57 import java.util.zip.ZipOutputStream;
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;
69 public class GatherAPIDataOld {
72 String srcName = "Current"; // default source name
73 String output; // name of output file to write
74 String base; // strip this prefix
81 public static int optionLength(String option) {
82 if (option.equals("-name")) {
84 } else if (option.equals("-output")) {
86 } else if (option.equals("-base")) {
88 } else if (option.equals("-filter")) {
90 } else if (option.equals("-zip")) {
92 } else if (option.equals("-gzip")) {
94 } else if (option.equals("-internal")) {
96 } else if (option.equals("-version")) {
102 public static boolean start(RootDoc root) {
103 return new GatherAPIDataOld(root).run();
106 GatherAPIDataOld(RootDoc root) {
109 String[][] options = root.options();
110 for (int i = 0; i < options.length; ++i) {
111 String opt = options[i][0];
112 if (opt.equals("-name")) {
113 this.srcName = options[i][1];
114 } else if (opt.equals("-output")) {
115 this.output = options[i][1];
116 } else if (opt.equals("-base")) {
117 this.base = options[i][1]; // should not include '.'
118 } else if (opt.equals("-filter")) {
119 this.pat = Pattern.compile(options[i][1], Pattern.CASE_INSENSITIVE);
120 } else if (opt.equals("-zip")) {
122 } else if (opt.equals("-gzip")) {
124 } else if (opt.equals("-internal")) {
125 this.internal = true;
126 } else if (opt.equals("-version")) {
131 results = new TreeSet(APIInfo.defaultComparator());
134 private boolean run() {
135 doDocs(root.classes());
137 OutputStream os = System.out;
138 if (output != null) {
139 ZipOutputStream zos = null;
142 zos = new ZipOutputStream(new FileOutputStream(output + ".zip"));
143 zos.putNextEntry(new ZipEntry(output));
146 os = new GZIPOutputStream(new FileOutputStream(output + ".gz"));
148 os = new FileOutputStream(output);
151 catch (IOException e) {
152 RuntimeException re = new RuntimeException(e.getMessage());
160 } catch (Exception e) {
167 BufferedWriter bw = null;
169 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
170 bw = new BufferedWriter(osw);
173 bw.write(String.valueOf(APIInfo.VERSION) + APIInfo.SEP); // header version
174 bw.write(srcName + APIInfo.SEP); // source name
175 bw.write((base == null ? "" : base) + APIInfo.SEP); // base
177 writeResults(results, bw);
178 bw.close(); // should flush, close all, etc
179 } catch (IOException e) {
180 try { bw.close(); } catch (IOException e2) {}
181 RuntimeException re = new RuntimeException("write error: " + e.getMessage());
189 private void doDocs(ProgramElementDoc[] docs) {
190 if (docs != null && docs.length > 0) {
191 for (int i = 0; i < docs.length; ++i) {
197 private void doDoc(ProgramElementDoc doc) {
198 if (ignore(doc)) return;
200 if (doc.isClass() || doc.isInterface()) {
201 ClassDoc cdoc = (ClassDoc)doc;
202 doDocs(cdoc.fields());
203 doDocs(cdoc.constructors());
204 doDocs(cdoc.methods());
205 doDocs(cdoc.innerClasses());
208 APIInfo info = createInfo(doc);
214 private boolean ignore(ProgramElementDoc doc) {
215 if (doc == null) return true;
216 if (doc.isPrivate() || doc.isPackagePrivate()) return true;
217 if (doc instanceof ConstructorDoc && ((ConstructorDoc)doc).isSynthetic()) return true;
218 if (doc.qualifiedName().indexOf(".misc") != -1) {
219 System.out.println("misc: " + doc.qualifiedName()); return true;
221 if (!internal) { // debug
222 Tag[] tags = doc.tags();
223 for (int i = 0; i < tags.length; ++i) {
224 if (tagKindIndex(tags[i].kind()) == INTERNAL) { return true; }
227 if (pat != null && (doc.isClass() || doc.isInterface())) {
228 if (!pat.matcher(doc.name()).matches()) {
235 private static void writeResults(Collection c, BufferedWriter w) {
236 Iterator iter = c.iterator();
237 while (iter.hasNext()) {
238 APIInfo info = (APIInfo)iter.next();
243 private String trimBase(String arg) {
245 for (int n = arg.indexOf(base); n != -1; n = arg.indexOf(base, n)) {
246 arg = arg.substring(0, n) + arg.substring(n+base.length());
252 public APIInfo createInfo(ProgramElementDoc doc) {
255 // Doc. isField, isMethod, isConstructor, isClass, isInterface
256 // ProgramElementDoc. containingClass, containingPackage
257 // ProgramElementDoc. isPublic, isProtected, isPrivate, isPackagePrivate
258 // ProgramElementDoc. isStatic, isFinal
259 // MemberDoc.isSynthetic
260 // ExecutableMemberDoc isSynchronized, signature
261 // Type.toString() // e.g. "String[][]"
262 // ClassDoc.isAbstract, superClass, interfaces, fields, methods, constructors, innerClasses
264 // ConstructorDoc qualifiedName
265 // MethodDoc isAbstract, returnType
267 APIInfo info = new APIInfo();
269 info.includeStatusVersion(true);
273 String[] version = new String[1];
274 info.setType(APIInfo.STA, tagStatus(doc, version));
275 info.setStatusVersion(version[0]);
278 if (doc.isPublic()) {
280 } else if (doc.isProtected()) {
282 } else if (doc.isPrivate()) {
285 // default is package
289 if (doc.isStatic()) {
292 // default is non-static
299 // default is non-final
305 } else if (doc.isMethod()) {
307 } else if (doc.isConstructor()) {
308 info.setConstructor();
309 } else if (doc.isClass() || doc.isInterface()) {
313 info.setPackage(trimBase(doc.containingPackage().name()));
314 info.setClassName((doc.isClass() || doc.isInterface() || (doc.containingClass() == null))
316 : trimBase(doc.containingClass().name()));
317 info.setName(trimBase(doc.name()));
319 if (doc instanceof FieldDoc) {
320 FieldDoc fdoc = (FieldDoc)doc;
321 info.setSignature(trimBase(fdoc.type().toString()));
322 } else if (doc instanceof ClassDoc) {
323 ClassDoc cdoc = (ClassDoc)doc;
325 if (cdoc.isClass() && cdoc.isAbstract()) {
326 // interfaces are abstract by default, don't mark them as abstract
330 StringBuffer buf = new StringBuffer();
331 if (cdoc.isClass()) {
332 buf.append("extends ");
333 buf.append(cdoc.superclass().qualifiedName());
335 ClassDoc[] imp = cdoc.interfaces();
336 if (imp != null && imp.length > 0) {
337 if (buf.length() > 0) {
340 buf.append("implements");
341 for (int i = 0; i < imp.length; ++i) {
346 buf.append(imp[i].qualifiedName());
349 info.setSignature(trimBase(buf.toString()));
351 ExecutableMemberDoc emdoc = (ExecutableMemberDoc)doc;
352 if (emdoc.isSynchronized()) {
353 info.setSynchronized();
356 if (doc instanceof MethodDoc) {
357 MethodDoc mdoc = (MethodDoc)doc;
358 if (mdoc.isAbstract()) {
361 info.setSignature(trimBase(mdoc.returnType().toString() + emdoc.signature()));
364 info.setSignature(trimBase(emdoc.signature()));
371 private int tagStatus(final Doc doc, String[] version) {
376 if (val == APIInfo.STA_DEPRECATED) {
377 // ok to have both a 'standard' tag and deprecated
379 } else if (res != APIInfo.STA_DEPRECATED) {
380 // if already not deprecated, this is an error
381 System.err.println("bad doc: " + doc + " both: " + APIInfo.getTypeValName(APIInfo.STA, res) + " and: " + APIInfo.getTypeValName(APIInfo.STA, val));
385 // ok to replace with new tag
390 System.err.println("warning: no tag for " + doc);
397 Tag[] tags = doc.tags();
398 Result result = new Result();
399 String statusVer = "";
400 for (int i = 0; i < tags.length; ++i) {
403 String kind = tag.kind();
404 int ix = tagKindIndex(kind);
408 result.set(internal ? APIInfo.STA_INTERNAL : -2); // -2 for legacy compatibility
409 statusVer = getStatusVersion(tag);
413 result.set(APIInfo.STA_DRAFT);
414 statusVer = getStatusVersion(tag);
418 result.set(APIInfo.STA_STABLE);
419 statusVer = getStatusVersion(tag);
423 result.set(APIInfo.STA_DEPRECATED);
424 statusVer = getStatusVersion(tag);
428 result.set(APIInfo.STA_OBSOLETE);
429 statusVer = getStatusVersion(tag);
445 throw new RuntimeException("unknown index " + ix + " for tag: " + kind);
449 if (version != null) {
450 version[0] = statusVer;
455 private String getStatusVersion(Tag tag) {
456 String text = tag.text();
457 if (text != null && text.length() > 0) {
458 // Extract version string
461 for (; i < text.length(); i++) {
462 char ch = text.charAt(i);
463 if (ch == '.' || (ch >= '0' && ch <= '9')) {
467 } else if (start != -1) {
472 return text.substring(start, i);
478 private static final int UNKNOWN = -1;
479 private static final int INTERNAL = 0;
480 private static final int DRAFT = 1;
481 private static final int STABLE = 2;
482 private static final int SINCE = 3;
483 private static final int DEPRECATED = 4;
484 private static final int AUTHOR = 5;
485 private static final int SEE = 6;
486 private static final int VERSION = 7;
487 private static final int PARAM = 8;
488 private static final int RETURN = 9;
489 private static final int THROWS = 10;
490 private static final int OBSOLETE = 11;
491 private static final int EXCEPTION = 12;
492 private static final int SERIAL = 13;
494 private static int tagKindIndex(String kind) {
495 final String[] tagKinds = {
496 "@internal", "@draft", "@stable", "@since", "@deprecated", "@author", "@see", "@version",
497 "@param", "@return", "@throws", "@obsolete", "@exception", "@serial"
500 for (int i = 0; i < tagKinds.length; ++i) {
501 if (kind.equals(tagKinds[i])) {