]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/dev/tool/docs/CheckAPI.java
go
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / dev / tool / docs / CheckAPI.java
1 /**\r
2 *******************************************************************************\r
3 * Copyright (C) 2004-2007, 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 invocation:\r
23  * c:/j2sdk1.4.2/bin/javadoc \r
24  *   -classpath c:/jd2sk1.4.2/lib/tools.jar \r
25  *   -doclet com.ibm.icu.dev.tool.docs.CheckAPI \r
26  *   -docletpath c:/doug/cvsproj/icu4j/src \r
27  *   -sourcepath c:/eclipse2.1/workspace2/icu4j/src \r
28  *   -compare c:/doug/cvsproj/icu4j/src/com/ibm/icu/dev/tool/docs/api2_6_1.txt \r
29  *   -output foo \r
30  *   com.ibm.icu.text\r
31  *\r
32  * todo: separate generation of data files (which requires taglet) from \r
33  * comparison and report generation (which does not require it)\r
34  * todo: provide command-line control of filters of which subclasses/packages to process\r
35  * todo: record full inheritance heirarchy, not just immediate inheritance \r
36  * todo: allow for aliasing comparisons (force (pkg.)*class to be treated as though it \r
37  * were in a different pkg/class heirarchy (facilitates comparison of icu4j and java)\r
38  */\r
39 \r
40 package com.ibm.icu.dev.tool.docs;\r
41 \r
42 import com.sun.javadoc.*;\r
43 import java.io.*;\r
44 import java.util.*;\r
45 \r
46 public class CheckAPI {\r
47     RootDoc root;\r
48     String compare; // file\r
49     String compareName;\r
50     TreeSet compareSet;\r
51     TreeSet results;\r
52     boolean html;\r
53     String srcName = "Current"; // default source name\r
54     String output;\r
55     \r
56     private static final int DATA_FILE_VERSION = 1;\r
57     private static final char SEP = ';';\r
58 \r
59     private static final int STA = 0, STA_DRAFT = 0, STA_STABLE = 1, STA_DEPRECATED = 2, STA_OBSOLETE = 3;\r
60     private static final int VIS = 1, VIS_PACKAGE = 0, VIS_PUBLIC= 1, VIS_PROTECTED = 2, VIS_PRIVATE = 3;\r
61     private static final int STK = 2, STK_STATIC = 1;\r
62     private static final int FIN = 3, FIN_FINAL = 1;\r
63     private static final int SYN = 4, SYN_SYNCHRONIZED = 1;\r
64     private static final int ABS = 5, ABS_ABSTRACT = 1;\r
65     private static final int CAT = 6, CAT_CLASS = 0, CAT_FIELD = 1, CAT_CONSTRUCTOR = 2, CAT_METHOD = 3;\r
66     private static final int PAK = 7;\r
67     private static final int CLS = 8;\r
68     private static final int NAM = 9;\r
69     private static final int SIG = 10;\r
70     private static final int EXC = 11;\r
71     private static final int NUM_TYPES = 11;\r
72 \r
73     static abstract class APIInfo {\r
74         public abstract int getVal(int typ);\r
75         public abstract String get(int typ, boolean brief);\r
76         public abstract void write(BufferedWriter w, boolean brief, boolean html, boolean detail);\r
77     }\r
78         \r
79     final static class Info extends APIInfo {\r
80         private int    info;\r
81         private String pack; // package\r
82         private String cls; // enclosing class\r
83         private String name; // name\r
84         private String sig;  // signature, class: inheritance, method: signature, field: type, const: signature\r
85         private String exc;  // throws \r
86         \r
87         public int getVal(int typ) {\r
88             validateType(typ);\r
89             return (info >> (typ*2)) & 0x3;\r
90         }\r
91 \r
92         public String get(int typ, boolean brief) {\r
93             validateType(typ);\r
94             String[] vals = brief ? shortNames[typ] : names[typ];\r
95             if (vals == null) {\r
96                 switch (typ) {\r
97                 case PAK: return pack;\r
98                 case CLS: return cls;\r
99                 case NAM: return name;\r
100                 case SIG: return sig;\r
101                 case EXC: return exc;\r
102                 }\r
103             }\r
104             int val = (info >> (typ*2)) & 0x3;\r
105             return vals[val];\r
106         }\r
107 \r
108         private void setType(int typ, int val) {\r
109             validateType(typ);\r
110             info &= ~(0x3 << (typ*2));\r
111             info |= (val&0x3) << (typ * 2);\r
112         }\r
113 \r
114         private void setType(int typ, String val) {\r
115             validateType(typ);\r
116             String[] vals = shortNames[typ];\r
117             if (vals == null) {\r
118                 switch (typ) {\r
119                 case PAK: pack = val; break;\r
120                 case CLS: cls = val; break;\r
121                 case NAM: name = val; break;\r
122                 case SIG: sig = val; break;\r
123                 case EXC: exc = val; break;\r
124                 }\r
125                 return;\r
126             }\r
127 \r
128             for (int i = 0; i < vals.length; ++i) {\r
129                 if (val.equalsIgnoreCase(vals[i])) {\r
130                     info &= ~(0x3 << (typ*2));\r
131                     info |= i << (typ*2);\r
132                     return;\r
133                 }\r
134             }\r
135 \r
136             throw new IllegalArgumentException("unrecognized value '" + val + "' for type '" + typeNames[typ] + "'");\r
137         }\r
138 \r
139         public void write(BufferedWriter w, boolean brief, boolean html, boolean detail) {\r
140             try {\r
141                 if (brief) {\r
142                     for (int i = 0; i < NUM_TYPES; ++i) {\r
143                         String s = get(i, true);\r
144                         if (s != null) {\r
145                             w.write(s);\r
146                         }\r
147                         w.write(SEP);\r
148                     }\r
149                 } else {\r
150                     // remove all occurrences of icu packages from the param string\r
151                     // fortunately, all the packages have 4 chars (lang, math, text, util).\r
152                     String xsig = sig;\r
153                     if (!detail) {\r
154                         final String ICUPACK = "com.ibm.icu.";\r
155                         StringBuffer buf = new StringBuffer();\r
156                         for (int i = 0; i < sig.length();) {\r
157                             int n = sig.indexOf(ICUPACK, i);\r
158                             if (n == -1) {\r
159                                 buf.append(sig.substring(i));\r
160                                 break;\r
161                             }\r
162                             buf.append(sig.substring(i, n));\r
163                             i = n + ICUPACK.length() + 5; // trailing 'xxxx.'\r
164                         }\r
165                         xsig = buf.toString();\r
166                     }\r
167 \r
168                     // construct signature\r
169                     for (int i = STA; i < CAT; ++i) { // include status\r
170                         String s = get(i, false);\r
171                         if (s != null && s.length() > 0) {\r
172                             if (i == STA) {\r
173                                 w.write('(');\r
174                                 w.write(s);\r
175                                 w.write(')');\r
176                             } else {\r
177                                 w.write(s);\r
178                             }\r
179                             w.write(' ');\r
180                         }\r
181                     }\r
182 \r
183                     int val = getVal(CAT);\r
184                     switch (val) {\r
185                     case CAT_CLASS:\r
186                         if (sig.indexOf("extends") == -1) {\r
187                             w.write("interface ");\r
188                         } else {\r
189                             w.write("class ");\r
190                         }\r
191                         if (cls.length() > 0) {\r
192                             w.write(cls);\r
193                             w.write('.');\r
194                         }\r
195                         w.write(name);\r
196                         if (detail) {\r
197                             w.write(' ');\r
198                             w.write(sig);\r
199                         }\r
200                         break;\r
201 \r
202                     case CAT_FIELD:\r
203                         w.write(xsig);\r
204                         w.write(' ');\r
205                         w.write(name);\r
206                         break;\r
207 \r
208                     case CAT_METHOD:\r
209                     case CAT_CONSTRUCTOR:\r
210                         int n = xsig.indexOf('(');\r
211                         if (n > 0) {\r
212                             w.write(xsig.substring(0, n));\r
213                             w.write(' ');\r
214                         } else {\r
215                             n = 0;\r
216                         }\r
217                         w.write(name);\r
218                         w.write(xsig.substring(n));\r
219                         break;\r
220                     }\r
221                 }\r
222                 w.newLine();\r
223             }\r
224             catch (IOException e) {\r
225                 RuntimeException re = new RuntimeException("IO Error");\r
226                 re.initCause(e);\r
227                 throw re;\r
228             }\r
229         }\r
230 \r
231         public boolean read(BufferedReader r) {\r
232             int i = 0;\r
233             try {\r
234                 for (; i < NUM_TYPES; ++i) {\r
235                     setType(i, readToken(r));\r
236                 }\r
237                 r.readLine(); // swallow line end sequence\r
238             }\r
239             catch (IOException e) {\r
240                 if (i == 0) { // assume if first read returns error, we have reached end of input\r
241                     return false;\r
242                 }\r
243                 RuntimeException re = new RuntimeException("IO Error");\r
244                 re.initCause(e);\r
245                 throw re;\r
246             }\r
247 \r
248             return true;\r
249         }\r
250 \r
251         public boolean read(ProgramElementDoc doc) {\r
252 \r
253             // Doc. name\r
254             // Doc. isField, isMethod, isConstructor, isClass, isInterface\r
255             // ProgramElementDoc. containingClass, containingPackage\r
256             // ProgramElementDoc. isPublic, isProtected, isPrivate, isPackagePrivate\r
257             // ProgramElementDoc. isStatic, isFinal\r
258             // MemberDoc.isSynthetic\r
259             // ExecutableMemberDoc isSynchronized, signature\r
260             // Type.toString() // e.g. "String[][]"\r
261             // ClassDoc.isAbstract, superClass, interfaces, fields, methods, constructors, innerClasses\r
262             // FieldDoc type\r
263             // ConstructorDoc qualifiedName\r
264             // MethodDoc isAbstract, returnType\r
265 \r
266             \r
267             // status\r
268             setType(STA, tagStatus(doc));\r
269 \r
270             // visibility\r
271             if (doc.isPublic()) {\r
272                 setType(VIS, VIS_PUBLIC);\r
273             } else if (doc.isProtected()) {\r
274                 setType(VIS, VIS_PROTECTED);\r
275             } else if (doc.isPrivate()) {\r
276                 setType(VIS, VIS_PRIVATE);\r
277             } else {\r
278                 // default is package\r
279             }\r
280 \r
281             // static\r
282             if (doc.isStatic()) {\r
283                 setType(STK, STK_STATIC);\r
284             } else {\r
285                 // default is non-static\r
286             }\r
287 \r
288             // final\r
289             if (doc.isFinal()) {\r
290                 setType(FIN, FIN_FINAL);\r
291             } else {\r
292                 // default is non-final\r
293             }\r
294 \r
295             // type\r
296             if (doc.isField()) {\r
297                 setType(CAT, CAT_FIELD);\r
298             } else if (doc.isMethod()) {\r
299                 setType(CAT, CAT_METHOD);\r
300             } else if (doc.isConstructor()) {\r
301                 setType(CAT, CAT_CONSTRUCTOR);\r
302             } else if (doc.isClass() || doc.isInterface()) {\r
303                 setType(CAT, CAT_CLASS);\r
304             }\r
305 \r
306             setType(PAK, doc.containingPackage().name());\r
307             setType(CLS, (doc.isClass() || doc.isInterface() || (doc.containingClass() == null)) ? "" : doc.containingClass().name());\r
308             setType(NAM, doc.name());\r
309 \r
310             if (doc instanceof FieldDoc) {\r
311                 FieldDoc fdoc = (FieldDoc)doc;\r
312                 setType(SIG, fdoc.type().toString());\r
313             } else if (doc instanceof ClassDoc) {\r
314                 ClassDoc cdoc = (ClassDoc)doc;\r
315 \r
316                 if (cdoc.isClass() && cdoc.isAbstract()) { // interfaces are abstract by default, don't mark them as abstract\r
317                     setType(ABS, ABS_ABSTRACT);\r
318                 }\r
319 \r
320                 StringBuffer buf = new StringBuffer();\r
321                 if (cdoc.isClass()) {\r
322                     buf.append("extends ");\r
323                     buf.append(cdoc.superclass().qualifiedName());\r
324                 }\r
325                 ClassDoc[] imp = cdoc.interfaces();\r
326                 if (imp != null && imp.length > 0) {\r
327                     if (buf.length() > 0) {\r
328                         buf.append(" ");\r
329                     }\r
330                     buf.append("implements");\r
331                     for (int i = 0; i < imp.length; ++i) {\r
332                         if (i != 0) {\r
333                             buf.append(",");\r
334                         }\r
335                         buf.append(" ");\r
336                         buf.append(imp[i].qualifiedName());\r
337                     }\r
338                 }\r
339                 setType(SIG, buf.toString());\r
340             } else {\r
341                 ExecutableMemberDoc emdoc = (ExecutableMemberDoc)doc;\r
342                 if (emdoc.isSynchronized()) {\r
343                     setType(SYN, SYN_SYNCHRONIZED);\r
344                 }\r
345 \r
346                 if (doc instanceof MethodDoc) {\r
347                     MethodDoc mdoc = (MethodDoc)doc;\r
348                     if (mdoc.isAbstract()) {\r
349                         setType(ABS, ABS_ABSTRACT);\r
350                     }\r
351                     setType(SIG, mdoc.returnType().toString() + emdoc.signature());\r
352                 } else {\r
353                     // constructor\r
354                     setType(SIG, emdoc.signature());\r
355                 }\r
356             }\r
357 \r
358             return true;\r
359         }\r
360 \r
361         public static Comparator defaultComparator() {\r
362             final Comparator c = new Comparator() {\r
363                     public int compare(Object lhs, Object rhs) {\r
364                         Info lhi = (Info)lhs;\r
365                         Info rhi = (Info)rhs;\r
366                         int result = lhi.pack.compareTo(rhi.pack);\r
367                         if (result == 0) {\r
368                             result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name : lhi.cls)\r
369                                 .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name : rhi.cls);\r
370                             if (result == 0) {\r
371                                 result = lhi.getVal(CAT)- rhi.getVal(CAT);\r
372                                 if (result == 0) {\r
373                                     result = lhi.name.compareTo(rhi.name);\r
374                                     if (result == 0) {\r
375                                         result = lhi.sig.compareTo(rhi.sig);\r
376                                     }\r
377                                 }\r
378                             }\r
379                         }\r
380                         return result;\r
381                     }\r
382                 };\r
383             return c;\r
384         }\r
385 \r
386         public static Comparator changedComparator() {\r
387             final Comparator c = new Comparator() {\r
388                     public int compare(Object lhs, Object rhs) {\r
389                         Info lhi = (Info)lhs;\r
390                         Info rhi = (Info)rhs;\r
391                         int result = lhi.pack.compareTo(rhi.pack);\r
392                         if (result == 0) {\r
393                             result = (lhi.getVal(CAT) == CAT_CLASS ? lhi.name : lhi.cls)\r
394                                 .compareTo(rhi.getVal(CAT) == CAT_CLASS ? rhi.name : rhi.cls);\r
395                             if (result == 0) {\r
396                                 result = lhi.getVal(CAT)- rhi.getVal(CAT);\r
397                                 if (result == 0) {\r
398                                     result = lhi.name.compareTo(rhi.name);\r
399                                     if (result == 0 && lhi.getVal(CAT) != CAT_CLASS) {\r
400                                         result = lhi.sig.compareTo(rhi.sig);\r
401                                     }\r
402                                 }\r
403                             }\r
404                         }\r
405                         return result;\r
406                     }\r
407                 };\r
408             return c;\r
409         }\r
410 \r
411         public static Comparator classFirstComparator() {\r
412             final Comparator c = new Comparator() {\r
413                     public int compare(Object lhs, Object rhs) {\r
414                         Info lhi = (Info)lhs;\r
415                         Info rhi = (Info)rhs;\r
416                         int result = lhi.pack.compareTo(rhi.pack);\r
417                         if (result == 0) {\r
418                             boolean lcls = lhi.getVal(CAT) == CAT_CLASS;\r
419                             boolean rcls = rhi.getVal(CAT) == CAT_CLASS;\r
420                             result = lcls == rcls ? 0 : (lcls ? -1 : 1);\r
421                             if (result == 0) {\r
422                                 result = (lcls ? lhi.name : lhi.cls).compareTo(rcls ? rhi.name : rhi.cls);\r
423                                 if (result == 0) {\r
424                                     result = lhi.getVal(CAT)- rhi.getVal(CAT);\r
425                                     if (result == 0) {\r
426                                         result = lhi.name.compareTo(rhi.name);\r
427                                         if (result == 0 && !lcls) {\r
428                                             result = lhi.sig.compareTo(rhi.sig);\r
429                                         }\r
430                                     }\r
431                                 }\r
432                             }\r
433                         }\r
434                         return result;\r
435                     }\r
436                 };\r
437             return c;\r
438         }\r
439 \r
440         private static final String[] typeNames = {\r
441             "status", "visibility", "static", "final", "synchronized", \r
442             "abstract", "category", "package", "class", "name", "signature"\r
443         };\r
444 \r
445         private static final String[][] names = {\r
446             { "draft     ", "stable    ", "deprecated", "obsolete  " },\r
447             { "package", "public", "protected", "private" },\r
448             { "", "static" },\r
449             { "", "final" },\r
450             { "", "synchronized" },\r
451             { "", "abstract" },\r
452             { "class", "field", "constructor", "method"  },\r
453             null,\r
454             null,\r
455             null,\r
456             null,\r
457             null\r
458         };\r
459 \r
460         private static final String[][] shortNames = {\r
461             { "DR", "ST", "DP", "OB" },\r
462             { "PK", "PB", "PT", "PR" },\r
463             { "NS", "ST" },\r
464             { "NF", "FN" },\r
465             { "NS", "SY" },\r
466             { "NA", "AB" },\r
467             { "L", "F", "C", "M" },\r
468             null,\r
469             null,\r
470             null,\r
471             null,\r
472             null\r
473         };\r
474 \r
475         private static void validateType(int typ) {\r
476             if (typ < 0 || typ > NUM_TYPES) {\r
477                 throw new IllegalArgumentException("bad type index: " + typ);\r
478             }\r
479         }\r
480 \r
481         public String toString() {\r
482             return get(NAM, true);\r
483         }\r
484     }\r
485 \r
486     static final class DeltaInfo extends APIInfo {\r
487         private Info a;\r
488         private Info b;\r
489 \r
490         DeltaInfo(Info a, Info b) {\r
491             this.a = a;\r
492             this.b = b;\r
493         }\r
494 \r
495         public int getVal(int typ) {\r
496             return a.getVal(typ);\r
497         }\r
498 \r
499         public String get(int typ, boolean brief) {\r
500             return a.get(typ, brief);\r
501         }\r
502 \r
503         public void write(BufferedWriter w, boolean brief, boolean html, boolean detail) {\r
504             a.write(w, brief, html, detail);\r
505             try {\r
506                 if (html) {\r
507                     w.write("<br>");\r
508                 }\r
509                 w.newLine();\r
510             } \r
511             catch (Exception e) {\r
512             }\r
513             b.write(w, brief, html, detail);\r
514         }\r
515 \r
516         public String toString() {\r
517             return a.get(NAM, true);\r
518         }\r
519     }\r
520 \r
521     public static int optionLength(String option) {\r
522         if (option.equals("-html")) {\r
523             return 1;\r
524         } else if (option.equals("-name")) {\r
525             return 2;\r
526         } else if (option.equals("-output")) {\r
527             return 2;\r
528         } else if (option.equals("-compare")) {\r
529             return 2;\r
530         }\r
531         return 0;\r
532     }\r
533 \r
534     public static boolean start(RootDoc root) {\r
535         return new CheckAPI(root).run();\r
536     }\r
537 \r
538     CheckAPI(RootDoc root) {\r
539         this.root = root;\r
540 \r
541         //      this.compare = "c:/doug/cvsproj/icu4j/src/com/ibm/icu/dev/tool/docs/api2_8.txt";\r
542 \r
543         String[][] options = root.options();\r
544         for (int i = 0; i < options.length; ++i) {\r
545             String opt = options[i][0];\r
546             if (opt.equals("-html")) {\r
547                 this.html = true;\r
548             } else if (opt.equals("-name")) {\r
549                 this.srcName = options[i][1];\r
550             } else if (opt.equals("-output")) {\r
551                 this.output = options[i][1];\r
552             } else if (opt.equals("-compare")) {\r
553                 this.compare = options[i][1];\r
554             }\r
555         }\r
556 \r
557         if (compare != null) {\r
558             try {\r
559                 // URL url = new URL(compare);\r
560                 File f = new File(compare);\r
561                 InputStream is = new FileInputStream(f);\r
562                 InputStreamReader isr = new InputStreamReader(is);\r
563                 BufferedReader br = new BufferedReader(isr);\r
564 \r
565                 // read header line\r
566                 /*int version = */Integer.parseInt(readToken(br));\r
567                 // check version if we change it later, probably can just rebuild though\r
568                 this.compareName = readToken(br);\r
569                 br.readLine();\r
570 \r
571                 // read data\r
572                 this.compareSet = new TreeSet(Info.defaultComparator());\r
573                 for (Info info = new Info(); info.read(br); info = new Info()) {\r
574                     compareSet.add(info);\r
575                 }\r
576             }\r
577             catch (Exception e) {\r
578                 RuntimeException re = new RuntimeException("error reading " + compare);\r
579                 re.initCause(e);\r
580                 throw re;\r
581             }\r
582         }\r
583             \r
584         results = new TreeSet(Info.defaultComparator());\r
585     }\r
586 \r
587     private boolean run() {\r
588         doDocs(root.classes());\r
589 \r
590         OutputStream os = System.out;\r
591         if (output != null) {\r
592             try {\r
593                 os = new FileOutputStream(output);\r
594             }\r
595             catch (FileNotFoundException e) {\r
596                 RuntimeException re = new RuntimeException(e.getMessage());\r
597                 re.initCause(e);\r
598                 throw re;\r
599             }\r
600         }\r
601 \r
602         BufferedWriter bw = null;\r
603         try {\r
604             OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");\r
605             bw = new BufferedWriter(osw);\r
606 \r
607             if (compareSet == null) {\r
608                 // writing data file\r
609                 bw.write(String.valueOf(DATA_FILE_VERSION) + SEP); // header version\r
610                 bw.write(srcName + SEP); // source name\r
611                 bw.newLine();\r
612                 writeResults(results, bw, true, false, false);\r
613             } else {\r
614                 // writing comparison info\r
615                 TreeSet removed = (TreeSet)compareSet.clone();\r
616                 removed.removeAll(results);\r
617 \r
618                 TreeSet added = (TreeSet)results.clone();\r
619                 added.removeAll(compareSet);\r
620 \r
621                 Iterator ai = added.iterator();\r
622                 Iterator ri = removed.iterator();\r
623                 ArrayList changed = new ArrayList();\r
624                 Comparator c = Info.changedComparator();\r
625                 Info a = null, r = null;\r
626                 while (ai.hasNext() && ri.hasNext()) {\r
627                     if (a == null) a = (Info)ai.next();\r
628                     if (r == null) r = (Info)ri.next();\r
629                     int result = c.compare(a, r);\r
630                     if (result < 0) {\r
631                         a = null;\r
632                     } else if (result > 0) {\r
633                         r = null;\r
634                     } else {\r
635                         changed.add(new DeltaInfo(a, r));\r
636                         a = null; ai.remove();\r
637                         r = null; ri.remove();\r
638                     }\r
639                 }\r
640 \r
641                 added = stripAndResort(added);\r
642                 removed = stripAndResort(removed);\r
643 \r
644                 if (html) {\r
645                     String title = "ICU4J API Comparison: " + srcName + " with " + compareName;\r
646 \r
647                     bw.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");\r
648                     bw.newLine();\r
649                     bw.write("<html>");\r
650                     bw.newLine();\r
651                     bw.write("<head>");\r
652                     bw.newLine();\r
653                     bw.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");\r
654                     bw.newLine();\r
655                     bw.write("<title>");\r
656                     bw.write(title);\r
657                     bw.write("</title>");\r
658                     bw.newLine();\r
659                     bw.write("<body>");\r
660                     bw.newLine();\r
661 \r
662                     bw.write("<h1>");\r
663                     bw.write(title);\r
664                     bw.write("</h1>");\r
665                     bw.newLine();\r
666 \r
667                     bw.write("<hr/>");\r
668                     bw.newLine();\r
669                     bw.write("<h2>");\r
670                     bw.write("Removed from " + compareName);\r
671                     bw.write("</h2>");\r
672                     bw.newLine();\r
673 \r
674                     if (removed.size() > 0) {\r
675                         writeResults(removed, bw, false, true, false);\r
676                     } else {\r
677                         bw.write("<p>(no API removed)</p>");\r
678                     }\r
679                     bw.newLine();\r
680 \r
681                     bw.write("<hr/>");\r
682                     bw.newLine();\r
683                     bw.write("<h2>");\r
684                     bw.write("Changed in " + srcName);\r
685                     bw.write("</h2>");\r
686                     bw.newLine();\r
687 \r
688                     if (changed.size() > 0) {\r
689                         writeResults(changed, bw, false, true, true);\r
690                     } else {\r
691                         bw.write("<p>(no API changed)</p>");\r
692                     }\r
693                     bw.newLine();\r
694 \r
695                     bw.write("<hr/>");\r
696                     bw.newLine();\r
697                     bw.write("<h2>");\r
698                     bw.write("Added in " + srcName);\r
699                     bw.write("</h2>");\r
700                     bw.newLine();\r
701 \r
702                     if (added.size() > 0) {\r
703                         writeResults(added, bw, false, true, false);\r
704                     } else {\r
705                         bw.write("<p>(no API added)</p>");\r
706                     }\r
707                     bw.write("<hr/>");\r
708                     bw.newLine();\r
709                     bw.write("<p><i>Contents generated by CheckAPI tool.<br/>Copyright (C) 2004, International Business Machines Corporation, All Rights Reserved.</i></p>");\r
710                     bw.newLine();\r
711                     bw.write("</body>");\r
712                     bw.newLine();\r
713                     bw.write("</html>");\r
714                     bw.newLine();\r
715                 } else {\r
716                     bw.write("Comparing " + srcName + " with " + compareName);\r
717                     bw.newLine();\r
718                     bw.newLine();\r
719 \r
720                     bw.newLine();\r
721                     bw.write("=== Removed from " + compareName + " ===");\r
722                     bw.newLine();\r
723                     if (removed.size() > 0) {\r
724                         writeResults(removed, bw, false, false, false);\r
725                     } else {\r
726                         bw.write("(no API removed)");\r
727                     }\r
728                     bw.newLine();\r
729 \r
730                     bw.newLine();\r
731                     bw.write("=== Changed in " + srcName + " ===");\r
732                     bw.newLine();\r
733                     if (changed.size() > 0) {\r
734                         writeResults(changed, bw, false, false, true);\r
735                     } else {\r
736                         bw.write("(no API changed)");\r
737                     }\r
738                     bw.newLine();\r
739 \r
740                     bw.newLine();\r
741                     bw.write("=== Added in " + srcName + " ===");\r
742                     bw.newLine();\r
743                     if (added.size() > 0) {\r
744                         writeResults(added, bw, false, false, false);\r
745                     } else {\r
746                         bw.write("(no API added)");\r
747                     }\r
748                     bw.newLine();\r
749                 }\r
750             }\r
751 \r
752             bw.close();\r
753         } catch (IOException e) {\r
754             try { bw.close(); } catch (IOException e2) {}\r
755             RuntimeException re = new RuntimeException("write error: " + e.getMessage());\r
756             re.initCause(e);\r
757             throw re;\r
758         }\r
759 \r
760         return false;\r
761     }\r
762 \r
763     private void doDocs(ProgramElementDoc[] docs) {\r
764         if (docs != null && docs.length > 0) {\r
765             for (int i = 0; i < docs.length; ++i) {\r
766                 doDoc(docs[i]);\r
767             }\r
768         }\r
769     }\r
770 \r
771     private void doDoc(ProgramElementDoc doc) {\r
772         if (ignore(doc)) return;\r
773 \r
774         if (doc.isClass() || doc.isInterface()) {\r
775             ClassDoc cdoc = (ClassDoc)doc;\r
776             doDocs(cdoc.fields());\r
777             doDocs(cdoc.constructors());\r
778             doDocs(cdoc.methods());\r
779             doDocs(cdoc.innerClasses());\r
780         }\r
781 \r
782         Info info = new Info();\r
783         if (info.read(doc)) {\r
784             results.add(info);\r
785         }\r
786     }\r
787 \r
788     private boolean ignore(ProgramElementDoc doc) {\r
789         if (doc == null) return true;\r
790         if (doc.isPrivate() || doc.isPackagePrivate()) return true;\r
791         if (doc instanceof ConstructorDoc && ((ConstructorDoc)doc).isSynthetic()) return true;\r
792         if (doc.qualifiedName().indexOf(".misc") != -1) return true;\r
793         Tag[] tags = doc.tags();\r
794         for (int i = 0; i < tags.length; ++i) {\r
795             if (tagKindIndex(tags[i].kind()) == INTERNAL) return true;\r
796         }\r
797 \r
798         return false;\r
799     }\r
800 \r
801     private static void writeResults(Collection c, BufferedWriter w, boolean brief, boolean html, boolean detail) {\r
802         Iterator iter = c.iterator();\r
803         String pack = null;\r
804         String clas = null;\r
805         while (iter.hasNext()) {\r
806             APIInfo info = (APIInfo)iter.next();\r
807             if (brief) {\r
808                 info.write(w, brief, false, detail);\r
809             } else {\r
810                 try {\r
811                     String p = info.get(PAK, true);\r
812                     if (!p.equals(pack)) {\r
813                         w.newLine();\r
814                         if (html) {\r
815                             if (clas != null) {\r
816                                 w.write("</ul>");\r
817                                 w.newLine();\r
818                             }\r
819                             if (pack != null) {\r
820                                 w.write("</ul>");\r
821                                 w.newLine();\r
822                             }\r
823                             \r
824                             w.write("<h3>Package ");\r
825                             w.write(p);\r
826                             w.write("</h3>");\r
827                             w.newLine();\r
828                             w.write("<ul>");\r
829                             w.newLine();\r
830                         } else {\r
831                             w.write("Package ");\r
832                             w.write(p);\r
833                             w.write(':');\r
834                         }\r
835                         w.newLine();\r
836                         w.newLine();\r
837                         \r
838                         pack = p;\r
839                         clas = null;\r
840                     }\r
841 \r
842                     if (info.getVal(CAT) != CAT_CLASS) {\r
843                         String name = info.get(CLS, true);\r
844                         if (!name.equals(clas)) {\r
845                             if (html) {\r
846                                 if (clas != null) {\r
847                                     w.write("</ul>");\r
848                                 }\r
849                                 w.write("<li>");\r
850                                 w.write(name);\r
851                                 w.newLine();\r
852                                 w.write("<ul>");\r
853                             } else {\r
854                                 w.write(name);\r
855                                 w.newLine();\r
856                             }\r
857                             clas = name;\r
858                         }\r
859                         w.write("    ");\r
860                     }\r
861                     if (html) {\r
862                         w.write("<li>");\r
863                         info.write(w, brief, html, detail);\r
864                         w.write("</li>");\r
865                     } else {\r
866                         info.write(w, brief, html, detail);\r
867                     }\r
868                 }\r
869                 catch (IOException e) {\r
870                     System.err.println("IOException " + e.getMessage() + " writing " + info);\r
871                 }\r
872             }\r
873         }\r
874         if (html) {\r
875             try {\r
876                 if (clas != null) {\r
877                     w.write("</ul>");\r
878                     w.newLine();\r
879                 }\r
880                 if (pack != null) {\r
881                     w.write("</ul>");\r
882                     w.newLine();\r
883                 }\r
884             } \r
885             catch (IOException e) {\r
886             }\r
887         }\r
888     }\r
889 \r
890     private static String readToken(BufferedReader r) throws IOException {\r
891         char[] buf = new char[256];\r
892         int i = 0;\r
893         for (; i < buf.length; ++i) {\r
894             int c = r.read();\r
895             if (c == -1) {\r
896                 throw new IOException("unexpected EOF");\r
897             } else if (c == SEP) {\r
898                 break;\r
899             }\r
900             buf[i] = (char)c;\r
901         }\r
902         if (i == buf.length) {\r
903             throw new IOException("unterminated token" + new String(buf));\r
904         }\r
905             \r
906         return new String(buf, 0, i);\r
907     }\r
908 \r
909     private static TreeSet stripAndResort(TreeSet t) {\r
910         stripClassInfo(t);\r
911         TreeSet r = new TreeSet(Info.classFirstComparator());\r
912         r.addAll(t);\r
913         return r;\r
914     }\r
915 \r
916     private static void stripClassInfo(Collection c) {\r
917         // c is sorted with class info first\r
918         Iterator iter = c.iterator();\r
919         String cname = null;\r
920         while (iter.hasNext()) {\r
921             Info info = (Info)iter.next();\r
922             String cls = info.get(CLS, true);\r
923             if (cname != null) {\r
924                 if (cname.equals(cls)) {\r
925                     iter.remove();\r
926                     continue;\r
927                 }\r
928                 cname = null;\r
929             } \r
930             if (info.getVal(CAT) == CAT_CLASS) {\r
931                 cname = info.get(NAM, true);\r
932             }\r
933         }\r
934     }\r
935 \r
936     private static int tagStatus(final Doc doc) {\r
937         class Result {\r
938             int res = -1;\r
939             void set(int val) { if (res != -1) throw new RuntimeException("bad doc: " + doc); res = val; }\r
940             int get() {\r
941                 if (res == -1) {\r
942                     System.err.println("warning: no tag for " + doc);\r
943                     return 0;\r
944                 }\r
945                 return res;\r
946             }\r
947         }\r
948 \r
949         Tag[] tags = doc.tags();\r
950         Result result = new Result();\r
951         for (int i = 0; i < tags.length; ++i) {\r
952             Tag tag = tags[i];\r
953 \r
954             String kind = tag.kind();\r
955             int ix = tagKindIndex(kind);\r
956 \r
957             switch (ix) {\r
958             case INTERNAL:\r
959                 result.set(-2);\r
960                 break;\r
961 \r
962             case DRAFT:\r
963                 result.set(STA_DRAFT);\r
964                 break;\r
965 \r
966             case STABLE:\r
967                 result.set(STA_STABLE);\r
968                 break;\r
969 \r
970             case DEPRECATED:\r
971                 result.set(STA_DEPRECATED);\r
972                 break;\r
973 \r
974             case OBSOLETE:\r
975                 result.set(STA_OBSOLETE);\r
976                 break;\r
977 \r
978             case SINCE:\r
979             case EXCEPTION:\r
980             case VERSION:\r
981             case UNKNOWN:\r
982             case AUTHOR:\r
983             case SEE:\r
984             case PARAM:\r
985             case RETURN:\r
986             case THROWS:\r
987             case SERIAL:\r
988                 break;\r
989 \r
990             default:\r
991                 throw new RuntimeException("unknown index " + ix + " for tag: " + kind);\r
992             }\r
993         }\r
994 \r
995         return result.get();\r
996     }\r
997 \r
998     private static final int UNKNOWN = -1;\r
999     private static final int INTERNAL = 0;\r
1000     private static final int DRAFT = 1;\r
1001     private static final int STABLE = 2;\r
1002     private static final int SINCE = 3;\r
1003     private static final int DEPRECATED = 4;\r
1004     private static final int AUTHOR = 5;\r
1005     private static final int SEE = 6;\r
1006     private static final int VERSION = 7;\r
1007     private static final int PARAM = 8;\r
1008     private static final int RETURN = 9;\r
1009     private static final int THROWS = 10;\r
1010     private static final int OBSOLETE = 11;\r
1011     private static final int EXCEPTION = 12;\r
1012     private static final int SERIAL = 13;\r
1013 \r
1014     private static int tagKindIndex(String kind) {\r
1015         final String[] tagKinds = {\r
1016             "@internal", "@draft", "@stable", "@since", "@deprecated", "@author", "@see", "@version",\r
1017             "@param", "@return", "@throws", "@obsolete", "@exception", "@serial"\r
1018         };\r
1019 \r
1020         for (int i = 0; i < tagKinds.length; ++i) {\r
1021             if (kind.equals(tagKinds[i])) {\r
1022                 return i;\r
1023             }\r
1024         }\r
1025         return UNKNOWN;\r
1026     }\r
1027 }\r