]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/dev/tool/docs/GatherAPIData.java
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / dev / tool / docs / GatherAPIData.java
1 /**\r
2  *******************************************************************************\r
3  * Copyright (C) 2004-2008, 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.4.2 or later\r
21  * \r
22  * Sample compilation:\r
23  * c:/doug/java/jdk1.4.2/build/windows-i586/bin/javac *.java\r
24  *\r
25  * Sample execution\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
31  *   -name "ICU4J 3.0"\r
32  *   -output icu4j30.api\r
33  *   -gzip\r
34  *   -source 1.4\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 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
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 com.sun.javadoc.*;\r
47 import java.io.*;\r
48 import java.util.*;\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
53 \r
54 public class GatherAPIData {\r
55     RootDoc root;\r
56     TreeSet results;\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
60     Pattern pat;\r
61     boolean zip;\r
62     boolean gzip;\r
63     boolean internal;\r
64     boolean version;\r
65 \r
66     public static int optionLength(String option) {\r
67         if (option.equals("-name")) {\r
68             return 2;\r
69         } else if (option.equals("-output")) {\r
70             return 2;\r
71         } else if (option.equals("-base")) {\r
72             return 2;\r
73         } else if (option.equals("-filter")) {\r
74             return 2;\r
75         } else if (option.equals("-zip")) {\r
76             return 1;\r
77         } else if (option.equals("-gzip")) {\r
78             return 1;\r
79         } else if (option.equals("-internal")) {\r
80             return 1;\r
81         } else if (option.equals("-version")) {\r
82             return 1;\r
83         }\r
84         return 0;\r
85     }\r
86 \r
87     public static boolean start(RootDoc root) {\r
88         return new GatherAPIData(root).run();\r
89     }\r
90 \r
91     GatherAPIData(RootDoc root) {\r
92         this.root = root;\r
93 \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
106                 this.zip = true;\r
107             } else if (opt.equals("-gzip")) {\r
108                 this.gzip = true;\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
113             }\r
114         }\r
115 \r
116         results = new TreeSet(APIInfo.defaultComparator());\r
117     }\r
118 \r
119     private boolean run() {\r
120         doDocs(root.classes());\r
121 \r
122         OutputStream os = System.out;\r
123         if (output != null) {\r
124             try {\r
125                 if (zip) {\r
126                     ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(output + ".zip"));\r
127                     zos.putNextEntry(new ZipEntry(output));\r
128                     os = zos;\r
129                 } else if (gzip) {\r
130                     os = new GZIPOutputStream(new FileOutputStream(output + ".gz"));\r
131                 } else {\r
132                     os = new FileOutputStream(output);\r
133                 }\r
134             }\r
135             catch (IOException e) {\r
136                 RuntimeException re = new RuntimeException(e.getMessage());\r
137                 re.initCause(e);\r
138                 throw re;\r
139             }\r
140         }\r
141 \r
142         BufferedWriter bw = null;\r
143         try {\r
144             OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");\r
145             bw = new BufferedWriter(osw);\r
146 \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
151             bw.newLine();\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
157             re.initCause(e);\r
158             throw re;\r
159         }\r
160 \r
161         return false;\r
162     }\r
163 \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
167                 doDoc(docs[i]);\r
168             }\r
169         }\r
170     }\r
171 \r
172     private void doDoc(ProgramElementDoc doc) {\r
173         if (ignore(doc)) return;\r
174 \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
181         }\r
182 \r
183         APIInfo info = createInfo(doc);\r
184         if (info != null) {\r
185             results.add(info);\r
186         }\r
187     }\r
188 \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
195         }\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
200             }\r
201         }\r
202         if (pat != null && (doc.isClass() || doc.isInterface())) {\r
203             if (!pat.matcher(doc.name()).matches()) {\r
204                 return true;\r
205             }\r
206         }\r
207         return false;\r
208     }\r
209 \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
214             info.writeln(w);\r
215         }\r
216     }\r
217 \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
222             }\r
223         }\r
224         return arg;\r
225     }\r
226 \r
227     public APIInfo createInfo(ProgramElementDoc doc) {\r
228 \r
229         // Doc. name\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
238         // FieldDoc type\r
239         // ConstructorDoc qualifiedName\r
240         // MethodDoc isAbstract, returnType\r
241 \r
242         APIInfo info = new APIInfo();\r
243         if (version) {\r
244             info.includeStatusVersion(true);\r
245         }\r
246             \r
247         // status\r
248         String[] version = new String[1];\r
249         info.setType(APIInfo.STA, tagStatus(doc, version));\r
250         info.setStatusVersion(version[0]);\r
251 \r
252         // visibility\r
253         if (doc.isPublic()) {\r
254             info.setPublic();\r
255         } else if (doc.isProtected()) {\r
256             info.setProtected();\r
257         } else if (doc.isPrivate()) {\r
258             info.setPrivate();\r
259         } else {\r
260             // default is package\r
261         }\r
262 \r
263         // static\r
264         if (doc.isStatic()) {\r
265             info.setStatic();\r
266         } else {\r
267             // default is non-static\r
268         }\r
269 \r
270         // final\r
271         if (doc.isFinal()) {\r
272             info.setFinal();\r
273         } else {\r
274             // default is non-final\r
275         }\r
276 \r
277         // type\r
278         if (doc.isField()) {\r
279             info.setField();\r
280         } else if (doc.isMethod()) {\r
281             info.setMethod();\r
282         } else if (doc.isConstructor()) {\r
283             info.setConstructor();\r
284         } else if (doc.isClass() || doc.isInterface()) {\r
285             info.setClass();\r
286         }\r
287 \r
288         info.setPackage(trimBase(doc.containingPackage().name()));\r
289         info.setClassName((doc.isClass() || doc.isInterface() || (doc.containingClass() == null)) \r
290                           ? "" \r
291                           : trimBase(doc.containingClass().name()));\r
292         info.setName(trimBase(doc.name()));\r
293 \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
299 \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
303             }\r
304 \r
305             StringBuffer buf = new StringBuffer();\r
306             if (cdoc.isClass()) {\r
307                 buf.append("extends ");\r
308                 buf.append(cdoc.superclass().qualifiedName());\r
309             }\r
310             ClassDoc[] imp = cdoc.interfaces();\r
311             if (imp != null && imp.length > 0) {\r
312                 if (buf.length() > 0) {\r
313                     buf.append(" ");\r
314                 }\r
315                 buf.append("implements");\r
316                 for (int i = 0; i < imp.length; ++i) {\r
317                     if (i != 0) {\r
318                         buf.append(",");\r
319                     }\r
320                     buf.append(" ");\r
321                     buf.append(imp[i].qualifiedName());\r
322                 }\r
323             }\r
324             info.setSignature(trimBase(buf.toString()));\r
325         } else {\r
326             ExecutableMemberDoc emdoc = (ExecutableMemberDoc)doc;\r
327             if (emdoc.isSynchronized()) {\r
328                 info.setSynchronized();\r
329             }\r
330 \r
331             if (doc instanceof MethodDoc) {\r
332                 MethodDoc mdoc = (MethodDoc)doc;\r
333                 if (mdoc.isAbstract()) {\r
334                     info.setAbstract();\r
335                 }\r
336                 info.setSignature(trimBase(mdoc.returnType().toString() + emdoc.signature()));\r
337             } else {\r
338                 // constructor\r
339                 info.setSignature(trimBase(emdoc.signature()));\r
340             }\r
341         }\r
342 \r
343         return info;\r
344     }\r
345 \r
346     private int tagStatus(final Doc doc, String[] version) {\r
347         class Result {\r
348             int res = -1;\r
349             void set(int val) { \r
350                 if (res != -1) {\r
351                     if (val == APIInfo.STA_DEPRECATED) {\r
352                         // ok to have both a 'standard' tag and deprecated\r
353                         return;\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
357                         return;\r
358                     }\r
359                 }\r
360                 // ok to replace with new tag\r
361                 res = val;\r
362             }\r
363             int get() {\r
364                 if (res == -1) {\r
365                     System.err.println("warning: no tag for " + doc);\r
366                     return 0;\r
367                 }\r
368                 return res;\r
369             }\r
370         }\r
371 \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
376             Tag tag = tags[i];\r
377 \r
378             String kind = tag.kind();\r
379             int ix = tagKindIndex(kind);\r
380 \r
381             switch (ix) {\r
382             case INTERNAL:\r
383                 result.set(internal ? APIInfo.STA_INTERNAL : -2); // -2 for legacy compatibility\r
384                 statusVer = getStatusVersion(tag);\r
385                 break;\r
386 \r
387             case DRAFT:\r
388                 result.set(APIInfo.STA_DRAFT);\r
389                 statusVer = getStatusVersion(tag);\r
390                 break;\r
391 \r
392             case STABLE:\r
393                 result.set(APIInfo.STA_STABLE);\r
394                 statusVer = getStatusVersion(tag);\r
395                 break;\r
396 \r
397             case DEPRECATED:\r
398                 result.set(APIInfo.STA_DEPRECATED);\r
399                 statusVer = getStatusVersion(tag);\r
400                 break;\r
401 \r
402             case OBSOLETE:\r
403                 result.set(APIInfo.STA_OBSOLETE);\r
404                 statusVer = getStatusVersion(tag);\r
405                 break;\r
406 \r
407             case SINCE:\r
408             case EXCEPTION:\r
409             case VERSION:\r
410             case UNKNOWN:\r
411             case AUTHOR:\r
412             case SEE:\r
413             case PARAM:\r
414             case RETURN:\r
415             case THROWS:\r
416             case SERIAL:\r
417                 break;\r
418 \r
419             default:\r
420                 throw new RuntimeException("unknown index " + ix + " for tag: " + kind);\r
421             }\r
422         }\r
423 \r
424         if (version != null) {\r
425             version[0] = statusVer;\r
426         }\r
427         return result.get();\r
428     }\r
429 \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
434             int start = -1;\r
435             int i = 0;\r
436             for (; i < text.length(); i++) {\r
437                 char ch = text.charAt(i);\r
438                 if (ch == '.' || (ch >= '0' && ch <= '9')) {\r
439                     if (start == -1) {\r
440                         start = i;\r
441                     }\r
442                 } else if (start != -1) {\r
443                     break;\r
444                 }\r
445             }\r
446             if (start != -1) {\r
447                 return text.substring(start, i);\r
448             }\r
449         }\r
450         return "";\r
451     }\r
452 \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
468 \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
473         };\r
474 \r
475         for (int i = 0; i < tagKinds.length; ++i) {\r
476             if (kind.equals(tagKinds[i])) {\r
477                 return i;\r
478             }\r
479         }\r
480         return UNKNOWN;\r
481     }\r
482 }\r