]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/tools/build/src/com/ibm/icu/dev/tool/docs/GatherAPIData.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / tools / build / src / com / ibm / icu / dev / tool / docs / GatherAPIData.java
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.5 or later
21  * 
22  * Sample compilation:
23  * c:/doug/java/jdk1.5/build/windows-i586/bin/javac *.java
24  *
25  * Sample execution
26  * c:/j2sdk1.5/bin/javadoc
27  *   -classpath c:/jd2sk1.5/lib/tools.jar 
28  *   -doclet com.ibm.icu.dev.tool.docs.GatherAPIData
29  *   -docletpath c:/doug/icu4j/tools/build/out/lib/icu4j-build-tools.jar
30  *   -sourcepath c:/doug/icu4j/main/classes/core/src 
31  *   -name "ICU4J 4.2"
32  *   -output icu4j42.api2
33  *   -gzip
34  *   -source 1.5
35  *   com.ibm.icu.lang com.ibm.icu.math com.ibm.icu.text com.ibm.icu.util
36  *
37  * todo: provide command-line control of filters of which subclasses/packages to process
38  * todo: record full inheritance hierarchy, 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 hierarchy (facilitates comparison of icu4j and java)
41  */
42
43 package com.ibm.icu.dev.tool.docs;
44
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;
58
59 import com.sun.javadoc.ClassDoc;
60 import com.sun.javadoc.ConstructorDoc;
61 import com.sun.javadoc.ExecutableMemberDoc;
62 import com.sun.javadoc.FieldDoc;
63 import com.sun.javadoc.LanguageVersion;
64 import com.sun.javadoc.MemberDoc;
65 import com.sun.javadoc.MethodDoc;
66 import com.sun.javadoc.ProgramElementDoc;
67 import com.sun.javadoc.RootDoc;
68 import com.sun.javadoc.Tag;
69
70 public class GatherAPIData {
71     RootDoc root;
72     TreeSet results;
73     String srcName = "Current"; // default source name
74     String output; // name of output file to write
75     String base; // strip this prefix
76     Pattern pat;
77     boolean zip;
78     boolean gzip;
79     boolean internal;
80     boolean version;
81
82     public static int optionLength(String option) {
83         if (option.equals("-name")) {
84             return 2;
85         } else if (option.equals("-output")) {
86             return 2;
87         } else if (option.equals("-base")) {
88             return 2;
89         } else if (option.equals("-filter")) {
90             return 2;
91         } else if (option.equals("-zip")) {
92             return 1;
93         } else if (option.equals("-gzip")) {
94             return 1;
95         } else if (option.equals("-internal")) {
96             return 1;
97         } else if (option.equals("-version")) {
98             return 1;
99         }
100         return 0;
101     }
102
103     public static boolean start(RootDoc root) {
104         return new GatherAPIData(root).run();
105     }
106
107     /**
108      * If you don't do this, javadoc treats enums like regular classes!
109      * doesn't matter if you pass -source 1.5 or not.
110      */
111     public static LanguageVersion languageVersion() {
112         return LanguageVersion.JAVA_1_5;
113     }
114
115     GatherAPIData(RootDoc root) {
116         this.root = root;
117
118         String[][] options = root.options();
119         for (int i = 0; i < options.length; ++i) {
120             String opt = options[i][0];
121             if (opt.equals("-name")) {
122                 this.srcName = options[i][1];
123             } else if (opt.equals("-output")) {
124                 this.output = options[i][1];
125             } else if (opt.equals("-base")) {
126                 this.base = options[i][1]; // should not include '.'
127             } else if (opt.equals("-filter")) {
128                 this.pat = Pattern.compile(options[i][1], Pattern.CASE_INSENSITIVE);
129             } else if (opt.equals("-zip")) {
130                 this.zip = true;
131             } else if (opt.equals("-gzip")) {
132                 this.gzip = true;
133             } else if (opt.equals("-internal")) {
134                 this.internal = true;
135             } else if (opt.equals("-version")) {
136                 this.version = true;
137             }
138         }
139
140         results = new TreeSet(APIInfo.defaultComparator());
141     }
142
143     private boolean run() {
144         doDocs(root.classes());
145
146         OutputStream os = System.out;
147         if (output != null) {
148             try {
149                 if (zip) {
150                     ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(output + ".zip"));
151                     zos.putNextEntry(new ZipEntry(output));
152                     os = zos;
153                 } else if (gzip) {
154                     os = new GZIPOutputStream(new FileOutputStream(output + ".gz"));
155                 } else {
156                     os = new FileOutputStream(output);
157                 }
158             }
159             catch (IOException e) {
160                 RuntimeException re = new RuntimeException(e.getMessage());
161                 re.initCause(e);
162                 throw re;
163             }
164         }
165
166         BufferedWriter bw = null;
167         try {
168             OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
169             bw = new BufferedWriter(osw);
170
171             // writing data file
172             bw.write(String.valueOf(APIInfo.VERSION) + APIInfo.SEP); // header version
173             bw.write(srcName + APIInfo.SEP); // source name
174             bw.write((base == null ? "" : base) + APIInfo.SEP); // base
175             bw.newLine();
176             writeResults(results, bw);
177             bw.close(); // should flush, close all, etc
178         } catch (IOException e) {
179             try { bw.close(); } catch (IOException e2) {}
180             RuntimeException re = new RuntimeException("write error: " + e.getMessage());
181             re.initCause(e);
182             throw re;
183         }
184
185         return false;
186     }
187
188     private void doDocs(ProgramElementDoc[] docs) {
189         if (docs != null && docs.length > 0) {
190             for (int i = 0; i < docs.length; ++i) {
191                 doDoc(docs[i]);
192             }
193         }
194     }
195
196     private void doDoc(ProgramElementDoc doc) {
197         if (ignore(doc)) return;
198
199         if (doc.isClass() || doc.isInterface()) {
200             ClassDoc cdoc = (ClassDoc)doc;
201             doDocs(cdoc.fields());
202             doDocs(cdoc.constructors());
203             doDocs(cdoc.methods());
204             // don't call this to iterate over inner classes,
205             // root.classes already includes them
206             // doDocs(cdoc.innerClasses());
207         }
208
209         APIInfo info = createInfo(doc);
210         if (info != null) {
211             results.add(info);
212         }
213     }
214
215     // Sigh. Javadoc doesn't indicate when the compiler generates
216     // the values and valueOf enum methods.  The position of the
217     // method for these is not always the same as the position of
218     // the class, though it often is, so we can't use that.
219
220     private boolean isIgnoredEnumMethod(ProgramElementDoc doc) {
221         if (doc.isMethod() && doc.containingClass().isEnum()) {
222             // System.out.println("*** " + doc.qualifiedName() + " pos: " +
223             //                    doc.position().line() +
224             //                    " containined by: " +
225             //                    doc.containingClass().name() +
226             //                    " pos: " +
227             //                    doc.containingClass().position().line());
228             // return doc.position().line() == doc.containingClass().position().line();
229
230             String name = doc.name();
231             // assume we don't have enums that overload these method names.
232             return "values".equals(name) || "valueOf".equals(name);
233         }
234         return false;
235     }
236
237     // isSynthesized also doesn't seem to work.  Let's do this, documenting
238     // synthesized constructors for abstract classes is kind of weird.
239     // We can't actually tell if the constructor was synthesized or is
240     // actually in the docs, but this shouldn't matter.  We don't really
241     // care if we didn't properly document the draft status of
242     // default constructors for abstract classes.
243
244     private boolean isAbstractClassDefaultConstructor(ProgramElementDoc doc) {
245         return doc.isConstructor()
246             && doc.containingClass().isAbstract()
247             && "()".equals(((ConstructorDoc) doc).signature());
248     }
249
250     private boolean ignore(ProgramElementDoc doc) {
251         if (doc == null) return true;
252         if (doc.isPrivate() || doc.isPackagePrivate()) return true;
253         if (doc instanceof MemberDoc && ((MemberDoc)doc).isSynthetic()) return true;
254         if (doc.qualifiedName().indexOf(".misc") != -1) {
255             System.out.println("misc: " + doc.qualifiedName()); return true;
256         }
257         if (isIgnoredEnumMethod(doc)) {
258             return true;
259         }
260         if (isAbstractClassDefaultConstructor(doc)) {
261             return true;
262         }
263
264         if (false && doc.qualifiedName().indexOf("LocaleDisplayNames") != -1) {
265           System.err.print("*** " + doc.qualifiedName() + ":");
266           if (doc.isClass()) System.err.print(" class");
267           if (doc.isConstructor()) System.err.print(" constructor");
268           if (doc.isEnum()) System.err.print(" enum");
269           if (doc.isEnumConstant()) System.err.print(" enum_constant");
270           if (doc.isError()) System.err.print(" error");
271           if (doc.isException()) System.err.print(" exception");
272           if (doc.isField()) System.err.print(" field");
273           if (doc.isInterface()) System.err.print(" interface");
274           if (doc.isMethod()) System.err.print(" method");
275           if (doc.isOrdinaryClass()) System.err.print(" ordinary_class");
276           System.err.println();
277         }
278
279         if (!internal) { // debug
280             Tag[] tags = doc.tags();
281             for (int i = 0; i < tags.length; ++i) {
282                 if (tagKindIndex(tags[i].kind()) == INTERNAL) { return true; }
283             }
284         }
285         if (pat != null && (doc.isClass() || doc.isInterface())) {
286             if (!pat.matcher(doc.name()).matches()) {
287                 return true;
288             }
289         }
290         return false;
291     }
292
293     private static void writeResults(Collection c, BufferedWriter w) {
294         Iterator iter = c.iterator();
295         while (iter.hasNext()) {
296             APIInfo info = (APIInfo)iter.next();
297             info.writeln(w);
298         }
299     }
300
301     private String trimBase(String arg) {
302         if (base != null) {
303             for (int n = arg.indexOf(base); n != -1; n = arg.indexOf(base, n)) {
304                 arg = arg.substring(0, n) + arg.substring(n+base.length());
305             }
306         }
307         return arg;
308     }
309
310     public APIInfo createInfo(ProgramElementDoc doc) {
311
312         // Doc. name
313         // Doc. isField, isMethod, isConstructor, isClass, isInterface
314         // ProgramElementDoc. containingClass, containingPackage
315         // ProgramElementDoc. isPublic, isProtected, isPrivate, isPackagePrivate
316         // ProgramElementDoc. isStatic, isFinal
317         // MemberDoc.isSynthetic
318         // ExecutableMemberDoc isSynchronized, signature
319         // Type.toString() // e.g. "String[][]"
320         // ClassDoc.isAbstract, superClass, interfaces, fields, methods, constructors, innerClasses
321         // FieldDoc type
322         // ConstructorDoc qualifiedName
323         // MethodDoc isAbstract, returnType
324
325         APIInfo info = new APIInfo();
326         if (version) {
327             info.includeStatusVersion(true);
328         }
329
330         // status
331         String[] version = new String[1];
332         info.setType(APIInfo.STA, tagStatus(doc, version));
333         info.setStatusVersion(version[0]);
334
335         // visibility
336         if (doc.isPublic()) {
337             info.setPublic();
338         } else if (doc.isProtected()) {
339             info.setProtected();
340         } else if (doc.isPrivate()) {
341             info.setPrivate();
342         } else {
343             // default is package
344         }
345
346         // static
347         if (doc.isStatic()) {
348             info.setStatic();
349         } else {
350             // default is non-static
351         }
352
353         // final
354         if (doc.isFinal()) {
355             info.setFinal();
356         } else {
357             // default is non-final
358         }
359
360         // type
361         if (doc.isField()) {
362             info.setField();
363         } else if (doc.isMethod()) {
364             info.setMethod();
365         } else if (doc.isConstructor()) {
366             info.setConstructor();
367         } else if (doc.isClass() || doc.isInterface()) {
368             info.setClass();
369         }
370
371         info.setPackage(trimBase(doc.containingPackage().name()));
372         info.setClassName((doc.isClass() || doc.isInterface() || (doc.containingClass() == null))
373                           ? ""
374                           : trimBase(doc.containingClass().name()));
375         info.setName(trimBase(doc.name()));
376
377         if (doc instanceof FieldDoc) {
378             FieldDoc fdoc = (FieldDoc)doc;
379             info.setSignature(trimBase(fdoc.type().toString()));
380         } else if (doc instanceof ClassDoc) {
381             ClassDoc cdoc = (ClassDoc)doc;
382
383             if (cdoc.isClass() && cdoc.isAbstract()) {
384                 // interfaces are abstract by default, don't mark them as abstract
385                 info.setAbstract();
386             }
387
388             StringBuffer buf = new StringBuffer();
389             if (cdoc.isClass()) {
390                 buf.append("extends ");
391                 buf.append(cdoc.superclass().qualifiedName());
392             }
393             ClassDoc[] imp = cdoc.interfaces();
394             if (imp != null && imp.length > 0) {
395                 if (buf.length() > 0) {
396                     buf.append(" ");
397                 }
398                 buf.append("implements");
399                 for (int i = 0; i < imp.length; ++i) {
400                     if (i != 0) {
401                         buf.append(",");
402                     }
403                     buf.append(" ");
404                     buf.append(imp[i].qualifiedName());
405                 }
406             }
407             info.setSignature(trimBase(buf.toString()));
408         } else {
409             ExecutableMemberDoc emdoc = (ExecutableMemberDoc)doc;
410             if (emdoc.isSynchronized()) {
411                 info.setSynchronized();
412             }
413
414             if (doc instanceof MethodDoc) {
415                 MethodDoc mdoc = (MethodDoc)doc;
416                 if (mdoc.isAbstract()) {
417                     info.setAbstract();
418                 }
419                 info.setSignature(trimBase(mdoc.returnType().toString() + emdoc.signature()));
420             } else {
421                 // constructor
422                 info.setSignature(trimBase(emdoc.signature()));
423             }
424         }
425
426         return info;
427     }
428
429     private int tagStatus(final ProgramElementDoc doc, String[] version) {
430         class Result {
431             int res = -1;
432             void set(int val) {
433                 if (res != -1) {
434                     if (val == APIInfo.STA_DEPRECATED) {
435                         // ok to have both a 'standard' tag and deprecated
436                         return;
437                     } else if (res != APIInfo.STA_DEPRECATED) {
438                         // if already not deprecated, this is an error
439                         System.err.println("bad doc: " + doc + " both: "
440                                            + APIInfo.getTypeValName(APIInfo.STA, res) + " and: "
441                                            + APIInfo.getTypeValName(APIInfo.STA, val));
442                         return;
443                     }
444                 }
445                 // ok to replace with new tag
446                 res = val;
447             }
448             int get() {
449                 if (res == -1) {
450                     System.err.println("warning: no tag for " + doc);
451                     return 0;
452                 }
453                 return res;
454             }
455         }
456
457         Tag[] tags = doc.tags();
458         Result result = new Result();
459         String statusVer = "";
460         for (int i = 0; i < tags.length; ++i) {
461             Tag tag = tags[i];
462
463             String kind = tag.kind();
464             int ix = tagKindIndex(kind);
465
466             switch (ix) {
467             case INTERNAL:
468                 result.set(internal ? APIInfo.STA_INTERNAL : -2); // -2 for legacy compatibility
469                 statusVer = getStatusVersion(tag);
470                 break;
471
472             case DRAFT:
473                 result.set(APIInfo.STA_DRAFT);
474                 statusVer = getStatusVersion(tag);
475                 break;
476
477             case STABLE:
478                 result.set(APIInfo.STA_STABLE);
479                 statusVer = getStatusVersion(tag);
480                 break;
481
482             case DEPRECATED:
483                 result.set(APIInfo.STA_DEPRECATED);
484                 statusVer = getStatusVersion(tag);
485                 break;
486
487             case OBSOLETE:
488                 result.set(APIInfo.STA_OBSOLETE);
489                 statusVer = getStatusVersion(tag);
490                 break;
491
492             case SINCE:
493             case EXCEPTION:
494             case VERSION:
495             case UNKNOWN:
496             case AUTHOR:
497             case SEE:
498             case PARAM:
499             case RETURN:
500             case THROWS:
501             case SERIAL:
502                 break;
503
504             default:
505                 throw new RuntimeException("unknown index " + ix + " for tag: " + kind);
506             }
507         }
508
509         if (version != null) {
510             version[0] = statusVer;
511         }
512         return result.get();
513     }
514
515     private String getStatusVersion(Tag tag) {
516         String text = tag.text();
517         if (text != null && text.length() > 0) {
518             // Extract version string
519             int start = -1;
520             int i = 0;
521             for (; i < text.length(); i++) {
522                 char ch = text.charAt(i);
523                 if (ch == '.' || (ch >= '0' && ch <= '9')) {
524                     if (start == -1) {
525                         start = i;
526                     }
527                 } else if (start != -1) {
528                     break;
529                 }
530             }
531             if (start != -1) {
532                 return text.substring(start, i);
533             }
534         }
535         return "";
536     }
537
538     private static final int UNKNOWN = -1;
539     private static final int INTERNAL = 0;
540     private static final int DRAFT = 1;
541     private static final int STABLE = 2;
542     private static final int SINCE = 3;
543     private static final int DEPRECATED = 4;
544     private static final int AUTHOR = 5;
545     private static final int SEE = 6;
546     private static final int VERSION = 7;
547     private static final int PARAM = 8;
548     private static final int RETURN = 9;
549     private static final int THROWS = 10;
550     private static final int OBSOLETE = 11;
551     private static final int EXCEPTION = 12;
552     private static final int SERIAL = 13;
553
554     private static int tagKindIndex(String kind) {
555         final String[] tagKinds = {
556             "@internal", "@draft", "@stable", "@since", "@deprecated", "@author", "@see",
557             "@version", "@param", "@return", "@throws", "@obsolete", "@exception", "@serial"
558         };
559
560         for (int i = 0; i < tagKinds.length; ++i) {
561             if (kind.equals(tagKinds[i])) {
562                 return i;
563             }
564         }
565         return UNKNOWN;
566     }
567 }