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