2 *******************************************************************************
\r
3 * Copyright (C) 2002-2008, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
8 * This is a tool to check the tags on ICU4J files. In particular, we're looking for:
\r
10 * - methods that have no tags
\r
11 * - custom tags: @draft, @stable, @internal?
\r
12 * - standard tags: @since, @deprecated
\r
15 * '@draft ICU X.X.X'
\r
16 * '@stable ICU X.X.X'
\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
22 * flags names of classes and their members that have no tags or incorrect syntax.
\r
24 * Requires JDK 1.4 or later
\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
31 package com.ibm.icu.dev.tool.docs;
\r
33 import com.sun.javadoc.*;
\r
35 public class CheckTags {
\r
40 DocStack stack = new DocStack();
\r
43 private String header;
\r
44 private boolean printed;
\r
45 private boolean reportError;
\r
46 private int errorCount;
\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
54 public String toString() {
\r
56 " printed: " + printed +
\r
57 " reportError: " + reportError +
\r
58 " errorCount: " + errorCount;
\r
63 private DocNode[] stack;
\r
65 private boolean newline;
\r
67 public void push(String header, boolean reportError) {
\r
68 if (stack == null) {
\r
69 stack = new DocNode[5];
\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
77 if (stack[index] == null) {
\r
78 stack[index] = new DocNode();
\r
80 // System.out.println("reset [" + index + "] header: " + header + " report: " + reportError);
\r
81 stack[index++].reset(header, reportError);
\r
86 throw new IndexOutOfBoundsException();
\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
96 // propagate to parent
\r
98 stack[index-1].errorCount += ec;
\r
102 System.out.println(); // always since we always report number of errors
\r
106 public void output(String msg, boolean error, boolean newline) {
\r
107 output(msg, error, newline, index-1);
\r
110 void output(String msg, boolean error, boolean newline, int ix) {
\r
111 DocNode last = stack[ix];
\r
113 last.errorCount += 1;
\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
121 if (isShort || (brief && error)) {
\r
122 msg = null; // nuke error messages if we're brief, just report headers and totals
\r
124 for (int i = 0; i <= ix;) {
\r
125 DocNode n = stack[i];
\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
132 System.out.print(" ");
\r
136 System.out.print(n.header);
\r
138 this.newline = true;
\r
144 if (index == 0 && this.newline) {
\r
145 System.out.println();
\r
148 System.out.print("*** ");
\r
150 System.out.print(msg);
\r
154 this.newline = newline;
\r
158 public static boolean start(RootDoc root) {
\r
159 return new CheckTags(root).run();
\r
162 public static int optionLength(String option) {
\r
163 if (option.equals("-log")) {
\r
165 } else if (option.equals("-brief")) {
\r
167 } else if (option.equals("-short")) {
\r
173 CheckTags(RootDoc root) {
\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
181 } else if (opt.equals("-brief")) {
\r
183 } else if (opt.equals("-short")) {
\r
184 this.isShort = true;
\r
190 doDocs(root.classes(), "Package", true);
\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
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
216 static int tagKindIndex(String kind) {
\r
217 for (int i = 0; i < tagKinds.length; ++i) {
\r
218 if (kind.equals(tagKinds[i])) {
\r
225 boolean newline = false;
\r
227 void output(String msg, boolean error, boolean newline) {
\r
228 stack.output(msg, error, newline);
\r
232 output(null, false, false);
\r
236 output(null, false, true);
\r
239 void log(String msg) {
\r
240 output(msg, false, false);
\r
243 void logln(String msg) {
\r
244 output(msg, false, true);
\r
247 void err(String msg) {
\r
248 output(msg, true, false);
\r
251 void errln(String msg) {
\r
252 output(msg, true, true);
\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
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
270 void doDoc(ProgramElementDoc doc) {
\r
271 if (doc != null && (doc.isPublic() || doc.isProtected())
\r
272 && !(doc instanceof ConstructorDoc && ((ConstructorDoc)doc).isSynthetic())) {
\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
278 boolean isClass = doc.isClass() || doc.isInterface();
\r
280 if (!isShort || isClass) {
\r
285 header += (isClass ? doc.qualifiedName() : doc.name());
\r
286 if (doc instanceof ExecutableMemberDoc) {
\r
287 header += ((ExecutableMemberDoc)doc).flatSignature();
\r
289 if (!isShort || isClass) {
\r
292 stack.push(header, isClass);
\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
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
319 for (int i = 0; i < tags.length; ++i) {
\r
322 String kind = tag.kind();
\r
323 int ix = tagKindIndex(kind);
\r
327 errln("unknown kind: " + kind);
\r
331 foundRequiredTag = true;
\r
332 foundInternalTag = true;
\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
343 if (tag.text().indexOf("ICU") != 0) {
\r
347 retainAll |= (tag.text().indexOf("(retainAll)") != -1);
\r
351 foundProvisionalTag = true;
\r
355 foundDeprecatedTag = true;
\r
356 if (tag.text().indexOf("ICU") == 0) {
\r
357 foundRequiredTag = true;
\r
362 if (tag.text().indexOf("ICU") != 0) {
\r
365 foundObsoleteTag = true;
\r
366 foundRequiredTag = true;
\r
371 String text = tag.text();
\r
372 if (text.length() != 0 && text.indexOf("ICU") != 0) {
\r
375 foundRequiredTag = true;
\r
376 foundStableTag = true;
\r
385 logln("You really ought to use @throws, you know... :-)");
\r
400 errln("unknown index: " + ix);
\r
403 if (!foundRequiredTag) {
\r
404 errln("missing required tag [" + doc.position() + "]");
\r
406 if (foundInternalTag && !foundDeprecatedTag) {
\r
407 errln("internal tag missing deprecated");
\r
409 if (foundDraftTag && !(foundDeprecatedTag || foundProvisionalTag)) {
\r
410 errln("draft tag missing deprecated or provisional");
\r
412 if (foundObsoleteTag && !foundDeprecatedTag) {
\r
413 errln("obsolete tag missing deprecated");
\r
415 if (foundStableTag && foundDeprecatedTag) {
\r
416 logln("stable deprecated");
\r