]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/icu/dev/tool/docs/CheckTags.java
icu4jsrc
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / icu / dev / tool / docs / CheckTags.java
1 /**\r
2 *******************************************************************************\r
3 * Copyright (C) 2002-2008, International Business Machines Corporation and    *\r
4 * others. All Rights Reserved.                                                *\r
5 *******************************************************************************\r
6 */\r
7 /**\r
8  * This is a tool to check the tags on ICU4J files.  In particular, we're looking for:\r
9  *\r
10  * - methods that have no tags\r
11  * - custom tags: @draft, @stable, @internal?\r
12  * - standard tags: @since, @deprecated\r
13  *\r
14  * Syntax of tags:\r
15  * '@draft ICU X.X.X'\r
16  * '@stable ICU X.X.X'\r
17  * '@internal'\r
18  * '@since  (don't use)'\r
19  * '@obsolete ICU X.X.X'\r
20  * '@deprecated to be removed in ICU X.X. [Use ...]'\r
21  *\r
22  * flags names of classes and their members that have no tags or incorrect syntax.\r
23  *\r
24  * Requires JDK 1.4 or later\r
25  *\r
26  * Use build.xml 'checktags' ant target, or\r
27  * run from directory containing CheckTags.class as follows:\r
28  * javadoc -classpath ${JAVA_HOME}/lib/tools.jar -doclet CheckTags -sourcepath ${ICU4J_src} [packagenames]\r
29  */\r
30 \r
31 package com.ibm.icu.dev.tool.docs;\r
32 \r
33 import com.sun.javadoc.*;\r
34 \r
35 public class CheckTags {\r
36     RootDoc root;\r
37     boolean log;\r
38     boolean brief;\r
39     boolean isShort;\r
40     DocStack stack = new DocStack();\r
41 \r
42     class DocNode {\r
43         private String header;\r
44         private boolean printed;\r
45         private boolean reportError;\r
46         private int errorCount;\r
47 \r
48         public void reset(String header, boolean reportError) {\r
49             this.header = header;\r
50             this.printed = false;\r
51             this.errorCount = 0;\r
52             this.reportError = reportError;\r
53         }\r
54         public String toString() {\r
55             return header +\r
56                 " printed: " + printed +\r
57                 " reportError: " + reportError +\r
58                 " errorCount: " + errorCount;\r
59         }\r
60     }\r
61 \r
62     class DocStack {\r
63         private DocNode[] stack;\r
64         private int index;\r
65         private boolean newline;\r
66 \r
67         public void push(String header, boolean reportError) {\r
68             if (stack == null) {\r
69                 stack = new DocNode[5];\r
70             } else {\r
71                 if (index == stack.length) {\r
72                     DocNode[] temp = new DocNode[stack.length * 2];\r
73                     System.arraycopy(stack, 0, temp, 0, index);\r
74                     stack = temp;\r
75                 }\r
76             }\r
77             if (stack[index] == null) {\r
78                 stack[index] = new DocNode();\r
79             }\r
80             //  System.out.println("reset [" + index + "] header: " + header + " report: " + reportError);\r
81             stack[index++].reset(header, reportError);\r
82         }\r
83 \r
84         public void pop() {\r
85             if (index == 0) {\r
86                 throw new IndexOutOfBoundsException();\r
87             }\r
88             --index;\r
89 \r
90             int ec = stack[index].errorCount; // index already decremented\r
91             if (ec > 0 || index == 0) { // always report for outermost element\r
92                 if (stack[index].reportError) {\r
93                     output("(" + ec + (ec == 1 ? " error" : " errors") + ")", false, true, index);\r
94                 }\r
95 \r
96                 // propagate to parent\r
97                 if (index > 0) {\r
98                     stack[index-1].errorCount += ec;\r
99                 }\r
100             }\r
101             if (index == 0) {\r
102                 System.out.println(); // always since we always report number of errors\r
103             }\r
104         }\r
105 \r
106         public void output(String msg, boolean error, boolean newline) {\r
107             output(msg, error, newline, index-1);\r
108         }\r
109 \r
110         void output(String msg, boolean error, boolean newline, int ix) {\r
111             DocNode last = stack[ix];\r
112             if (error) {\r
113                 last.errorCount += 1;\r
114         }\r
115 \r
116             boolean show = !brief || last.reportError;\r
117             // boolean nomsg = show && brief && error;\r
118             //            System.out.println(">>> " + last + " error: " + error + " show: " + show + " nomsg: " + nomsg);\r
119 \r
120             if (show) {\r
121                 if (isShort || (brief && error)) {\r
122                     msg = null; // nuke error messages if we're brief, just report headers and totals\r
123                 }\r
124                 for (int i = 0; i <= ix;) {\r
125                     DocNode n = stack[i];\r
126                     if (n.printed) {\r
127                         if (msg != null || !last.printed) { // since index > 0 last is not null\r
128                             if (this.newline && i == 0) {\r
129                                 System.out.println();\r
130                                 this.newline = false;\r
131                             }\r
132                             System.out.print("  ");\r
133                         }\r
134                         ++i;\r
135                     } else {\r
136                         System.out.print(n.header);\r
137                         n.printed = true;\r
138                         this.newline = true;\r
139                         i = 0;\r
140                     }\r
141                 }\r
142 \r
143                 if (msg != null) {\r
144                     if (index == 0 && this.newline) {\r
145                         System.out.println();\r
146                     }\r
147                     if (error) {\r
148                         System.out.print("*** ");\r
149                     }\r
150                     System.out.print(msg);\r
151                 }\r
152             }\r
153 \r
154             this.newline = newline;\r
155         }\r
156     }\r
157 \r
158     public static boolean start(RootDoc root) {\r
159         return new CheckTags(root).run();\r
160     }\r
161 \r
162     public static int optionLength(String option) {\r
163         if (option.equals("-log")) {\r
164             return 1;\r
165         } else if (option.equals("-brief")) {\r
166             return 1;\r
167         } else if (option.equals("-short")) {\r
168             return 1;\r
169         }\r
170         return 0;\r
171     }\r
172 \r
173     CheckTags(RootDoc root) {\r
174         this.root = root;\r
175 \r
176         String[][] options = root.options();\r
177         for (int i = 0; i < options.length; ++i) {\r
178             String opt = options[i][0];\r
179             if (opt.equals("-log")) {\r
180                 this.log = true;\r
181             } else if (opt.equals("-brief")) {\r
182                 this.brief = true;\r
183             } else if (opt.equals("-short")) {\r
184                 this.isShort = true;\r
185             }\r
186         }\r
187     }\r
188 \r
189     boolean run() {\r
190         doDocs(root.classes(), "Package", true);\r
191         return false;\r
192     }\r
193 \r
194     static final String[] tagKinds = {\r
195         "@internal", "@draft", "@stable", "@since", "@deprecated", "@author", "@see", "@version",\r
196         "@param", "@return", "@throws", "@obsolete", "@exception", "@serial", "@provisional"\r
197     };\r
198 \r
199     static final int UNKNOWN = -1;\r
200     static final int INTERNAL = 0;\r
201     static final int DRAFT = 1;\r
202     static final int STABLE = 2;\r
203     static final int SINCE = 3;\r
204     static final int DEPRECATED = 4;\r
205     static final int AUTHOR = 5;\r
206     static final int SEE = 6;\r
207     static final int VERSION = 7;\r
208     static final int PARAM = 8;\r
209     static final int RETURN = 9;\r
210     static final int THROWS = 10;\r
211     static final int OBSOLETE = 11;\r
212     static final int EXCEPTION = 12;\r
213     static final int SERIAL = 13;\r
214     static final int PROVISIONAL = 14;\r
215 \r
216     static int tagKindIndex(String kind) {\r
217         for (int i = 0; i < tagKinds.length; ++i) {\r
218             if (kind.equals(tagKinds[i])) {\r
219                 return i;\r
220             }\r
221         }\r
222         return UNKNOWN;\r
223     }\r
224 \r
225     boolean newline = false;\r
226 \r
227     void output(String msg, boolean error, boolean newline) {\r
228         stack.output(msg, error, newline);\r
229     }\r
230 \r
231     void log() {\r
232         output(null, false, false);\r
233     }\r
234 \r
235     void logln() {\r
236         output(null, false, true);\r
237     }\r
238 \r
239     void log(String msg) {\r
240         output(msg, false, false);\r
241     }\r
242 \r
243     void logln(String msg) {\r
244         output(msg, false, true);\r
245     }\r
246 \r
247     void err(String msg) {\r
248         output(msg, true, false);\r
249     }\r
250 \r
251     void errln(String msg) {\r
252         output(msg, true, true);\r
253     }\r
254 \r
255     void tagErr(Tag tag) {\r
256         // Tag.position() requires JDK 1.4, build.xml tests for this\r
257         errln(tag.toString() + " [" + tag.position() + "]");\r
258     }\r
259 \r
260     void doDocs(ProgramElementDoc[] docs, String header, boolean reportError) {\r
261         if (docs != null && docs.length > 0) {\r
262             stack.push(header, reportError);\r
263             for (int i = 0; i < docs.length; ++i) {\r
264                 doDoc(docs[i]);\r
265             }\r
266             stack.pop();\r
267         }\r
268     }\r
269 \r
270     void doDoc(ProgramElementDoc doc) {\r
271         if (doc != null && (doc.isPublic() || doc.isProtected())\r
272             && !(doc instanceof ConstructorDoc && ((ConstructorDoc)doc).isSynthetic())) {\r
273 \r
274             // unfortunately, in JDK 1.4.1 MemberDoc.isSynthetic is not properly implemented for\r
275             // synthetic constructors.  So you'll have to live with spurious errors or 'implement'\r
276             // the synthetic constructors...\r
277 \r
278             boolean isClass = doc.isClass() || doc.isInterface();\r
279             String header;\r
280             if (!isShort || isClass) {\r
281                 header = "--- ";\r
282             } else {\r
283                 header = "";\r
284             }\r
285             header += (isClass ? doc.qualifiedName() : doc.name());\r
286             if (doc instanceof ExecutableMemberDoc) {\r
287                 header += ((ExecutableMemberDoc)doc).flatSignature();\r
288             }\r
289             if (!isShort || isClass) {\r
290                 header += " ---";\r
291             }\r
292             stack.push(header, isClass);\r
293             if (log) {\r
294                 logln();\r
295             }\r
296             boolean recurse = doTags(doc);\r
297             if (recurse && isClass) {\r
298                 ClassDoc cdoc = (ClassDoc)doc;\r
299                 doDocs(cdoc.fields(), "Fields", !brief);\r
300                 doDocs(cdoc.constructors(), "Constructors", !brief);\r
301                 doDocs(cdoc.methods(), "Methods", !brief);\r
302             }\r
303             stack.pop();\r
304         }\r
305     }\r
306 \r
307     /** Return true if subelements of this doc should be checked */\r
308     boolean doTags(ProgramElementDoc doc) {\r
309         Tag[] tags = doc.tags();\r
310         boolean foundRequiredTag = false;\r
311         boolean foundDraftTag = false;\r
312         boolean foundProvisionalTag = false;\r
313         boolean foundDeprecatedTag = false;\r
314         boolean foundObsoleteTag = false;\r
315         boolean foundInternalTag = false;\r
316         boolean foundStableTag = false;\r
317         boolean retainAll = false;\r
318 \r
319         for (int i = 0; i < tags.length; ++i) {\r
320             Tag tag = tags[i];\r
321 \r
322             String kind = tag.kind();\r
323             int ix = tagKindIndex(kind);\r
324 \r
325             switch (ix) {\r
326             case UNKNOWN:\r
327                 errln("unknown kind: " + kind);\r
328                 break;\r
329 \r
330             case INTERNAL:\r
331                 foundRequiredTag = true;\r
332                 foundInternalTag = true;\r
333                 break;\r
334 \r
335             case DRAFT:\r
336                 foundRequiredTag = true;\r
337                 foundDraftTag = true;\r
338                 if (tag.text().indexOf("ICU 2.8") != -1 &&\r
339                     tag.text().indexOf("(retain") == -1) { // catch both retain and retainAll\r
340                     tagErr(tag);\r
341                     break;\r
342                 }\r
343                 if (tag.text().indexOf("ICU") != 0) {\r
344                     tagErr(tag);\r
345                     break;\r
346                 }\r
347                 retainAll |= (tag.text().indexOf("(retainAll)") != -1);\r
348                 break;\r
349 \r
350             case PROVISIONAL:\r
351                 foundProvisionalTag = true;\r
352                 break;\r
353 \r
354             case DEPRECATED:\r
355                 foundDeprecatedTag = true;\r
356                 if (tag.text().indexOf("ICU") == 0) {\r
357                     foundRequiredTag = true;\r
358                 }\r
359                 break;\r
360 \r
361             case OBSOLETE:\r
362                 if (tag.text().indexOf("ICU") != 0) {\r
363                     tagErr(tag);\r
364                 }\r
365                 foundObsoleteTag = true;\r
366                 foundRequiredTag = true;\r
367                 break;\r
368 \r
369             case STABLE:\r
370                 {\r
371                     String text = tag.text();\r
372                     if (text.length() != 0 && text.indexOf("ICU") != 0) {\r
373                         tagErr(tag);\r
374                     }\r
375                     foundRequiredTag = true;\r
376                     foundStableTag = true;\r
377                 }\r
378                 break;\r
379 \r
380             case SINCE:\r
381                 tagErr(tag);\r
382                 break;\r
383 \r
384             case EXCEPTION:\r
385                 logln("You really ought to use @throws, you know... :-)");\r
386 \r
387             case AUTHOR:\r
388             case SEE:\r
389             case PARAM:\r
390             case RETURN:\r
391             case THROWS:\r
392             case SERIAL:\r
393                 break;\r
394 \r
395             case VERSION:\r
396                 tagErr(tag);\r
397                 break;\r
398 \r
399             default:\r
400                 errln("unknown index: " + ix);\r
401             }\r
402         }\r
403         if (!foundRequiredTag) {\r
404             errln("missing required tag [" + doc.position() + "]");\r
405         }\r
406         if (foundInternalTag && !foundDeprecatedTag) {\r
407             errln("internal tag missing deprecated");\r
408         }\r
409         if (foundDraftTag && !(foundDeprecatedTag || foundProvisionalTag)) {\r
410             errln("draft tag missing deprecated or provisional");\r
411         }\r
412         if (foundObsoleteTag && !foundDeprecatedTag) {\r
413             errln("obsolete tag missing deprecated");\r
414         }\r
415         if (foundStableTag && foundDeprecatedTag) {\r
416             logln("stable deprecated");\r
417         }\r
418 \r
419         return !retainAll;\r
420     }\r
421 }\r