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