KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > puppycrawl > tools > checkstyle > checks > javadoc > JavadocMethodCheck


1 ////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code for adherence to a set of rules.
3
// Copyright (C) 2001-2005 Oliver Burn
4
//
5
// This library is free software; you can redistribute it and/or
6
// modify it under the terms of the GNU Lesser General Public
7
// License as published by the Free Software Foundation; either
8
// version 2.1 of the License, or (at your option) any later version.
9
//
10
// This library is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
// Lesser General Public License for more details.
14
//
15
// You should have received a copy of the GNU Lesser General Public
16
// License along with this library; if not, write to the Free Software
17
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
////////////////////////////////////////////////////////////////////////////////
19
package com.puppycrawl.tools.checkstyle.checks.javadoc;
20
21 import java.util.ArrayList JavaDoc;
22 import java.util.HashSet JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.ListIterator JavaDoc;
26 import java.util.Set JavaDoc;
27
28 import java.util.regex.Matcher JavaDoc;
29 import java.util.regex.Pattern JavaDoc;
30
31 import antlr.collections.AST;
32
33 import com.puppycrawl.tools.checkstyle.api.DetailAST;
34 import com.puppycrawl.tools.checkstyle.api.FileContents;
35 import com.puppycrawl.tools.checkstyle.api.FullIdent;
36 import com.puppycrawl.tools.checkstyle.api.Scope;
37 import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
38 import com.puppycrawl.tools.checkstyle.api.TextBlock;
39 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
40 import com.puppycrawl.tools.checkstyle.api.Utils;
41 import com.puppycrawl.tools.checkstyle.checks.AbstractTypeAwareCheck;
42 import com.puppycrawl.tools.checkstyle.checks.CheckUtils;
43
44 /**
45  * Checks the Javadoc of a method or constructor.
46  *
47  * @author Oliver Burn
48  * @author Rick Giles
49  * @author o_sukhodoslky
50  * @version 1.1
51  */

52 public class JavadocMethodCheck extends AbstractTypeAwareCheck
53 {
54     /** the pattern to match Javadoc tags that take an argument * */
55     private static final String JavaDoc MATCH_JAVADOC_ARG_PAT =
56         "@(throws|exception|param)\\s+(\\S+)\\s+\\S";
57     /** compiled regexp to match Javadoc tags that take an argument * */
58     private static final Pattern JavaDoc MATCH_JAVADOC_ARG = Utils
59             .createPattern(MATCH_JAVADOC_ARG_PAT);
60
61     /**
62      * the pattern to match the first line of a multi-line Javadoc tag that
63      * takes an argument.
64      */

65     private static final String JavaDoc MATCH_JAVADOC_ARG_MULTILINE_START_PAT =
66         "@(throws|exception|param)\\s+(\\S+)\\s*$";
67     /** compiled regexp to match first part of multilineJavadoc tags * */
68     private static final Pattern JavaDoc MATCH_JAVADOC_ARG_MULTILINE_START = Utils
69             .createPattern(MATCH_JAVADOC_ARG_MULTILINE_START_PAT);
70
71     /** the pattern that looks for a continuation of the comment * */
72     private static final String JavaDoc MATCH_JAVADOC_MULTILINE_CONT_PAT =
73         "(\\*/|@|[^\\s\\*])";
74     /** compiled regexp to look for a continuation of the comment * */
75     private static final Pattern JavaDoc MATCH_JAVADOC_MULTILINE_CONT = Utils
76             .createPattern(MATCH_JAVADOC_MULTILINE_CONT_PAT);
77     /** Multiline finished at end of comment * */
78     private static final String JavaDoc END_JAVADOC = "*/";
79     /** Multiline finished at next Javadoc * */
80     private static final String JavaDoc NEXT_TAG = "@";
81
82     /** the pattern to match Javadoc tags with no argument * */
83     private static final String JavaDoc MATCH_JAVADOC_NOARG_PAT =
84         "@(return|see)\\s+\\S";
85     /** compiled regexp to match Javadoc tags with no argument * */
86     private static final Pattern JavaDoc MATCH_JAVADOC_NOARG = Utils
87             .createPattern(MATCH_JAVADOC_NOARG_PAT);
88     /**
89      * the pattern to match the first line of a multi-line Javadoc tag that
90      * takes no argument.
91      */

92     private static final String JavaDoc MATCH_JAVADOC_NOARG_MULTILINE_START_PAT =
93         "@(return|see)\\s*$";
94     /** compiled regexp to match first part of multilineJavadoc tags * */
95     private static final Pattern JavaDoc MATCH_JAVADOC_NOARG_MULTILINE_START = Utils
96             .createPattern(MATCH_JAVADOC_NOARG_MULTILINE_START_PAT);
97
98     /** the pattern to match Javadoc tags with no argument and {} * */
99     private static final String JavaDoc MATCH_JAVADOC_NOARG_CURLY_PAT =
100         "\\{\\s*@(inheritDoc)\\s*\\}";
101     /** compiled regexp to match Javadoc tags with no argument and {} * */
102     private static final Pattern JavaDoc MATCH_JAVADOC_NOARG_CURLY = Utils
103             .createPattern(MATCH_JAVADOC_NOARG_CURLY_PAT);
104
105     /** Maximum children allowed * */
106     private static final int MAX_CHILDREN = 7;
107
108     /** Maximum children allowed * */
109     private static final int BODY_SIZE = 3;
110
111     /** the visibility scope where Javadoc comments are checked * */
112     private Scope mScope = Scope.PRIVATE;
113
114     /** the visibility scope where Javadoc comments shouldn't be checked * */
115     private Scope mExcludeScope;
116
117     /**
118      * controls whether to allow documented exceptions that are not declared if
119      * they are a subclass of java.lang.RuntimeException.
120      */

121     private boolean mAllowUndeclaredRTE;
122
123     /**
124      * controls whether to allow documented exceptions that are subclass of one
125      * of declared exception. Defaults to false (backward compatibility).
126      */

127     private boolean mAllowThrowsTagsForSubclasses;
128
129     /**
130      * controls whether to ignore errors when a method has parameters but does
131      * not have matching param tags in the javadoc. Defaults to false.
132      */

133     private boolean mAllowMissingParamTags;
134
135     /**
136      * controls whether to ignore errors when a method declares that it throws
137      * exceptions but does not have matching throws tags in the javadoc.
138      * Defaults to false.
139      */

140     private boolean mAllowMissingThrowsTags;
141
142     /**
143      * controls whether to ignore errors when a method returns non-void type but
144      * does not have a return tag in the javadoc. Defaults to false.
145      */

146     private boolean mAllowMissingReturnTag;
147
148     /**
149      * Controls whether to ignore errors when there is no javadoc. Defaults to
150      * false.
151      */

152     private boolean mAllowMissingJavadoc;
153
154     /**
155      * Controls whether to allow missing Javadoc on accessor methods for
156      * properties (setters and getters).
157      */

158     private boolean mAllowMissingPropertyJavadoc;
159
160     /**
161      * Set the scope.
162      *
163      * @param aFrom a <code>String</code> value
164      */

165     public void setScope(String JavaDoc aFrom)
166     {
167         mScope = Scope.getInstance(aFrom);
168     }
169
170     /**
171      * Set the excludeScope.
172      *
173      * @param aScope a <code>String</code> value
174      */

175     public void setExcludeScope(String JavaDoc aScope)
176     {
177         mExcludeScope = Scope.getInstance(aScope);
178     }
179
180     /**
181      * controls whether to allow documented exceptions that are not declared if
182      * they are a subclass of java.lang.RuntimeException.
183      *
184      * @param aFlag a <code>Boolean</code> value
185      */

186     public void setAllowUndeclaredRTE(boolean aFlag)
187     {
188         mAllowUndeclaredRTE = aFlag;
189     }
190
191     /**
192      * controls whether to allow documented exception that are subclass of one
193      * of declared exceptions.
194      *
195      * @param aFlag a <code>Boolean</code> value
196      */

197     public void setAllowThrowsTagsForSubclasses(boolean aFlag)
198     {
199         mAllowThrowsTagsForSubclasses = aFlag;
200     }
201
202     /**
203      * controls whether to allow a method which has parameters to omit matching
204      * param tags in the javadoc. Defaults to false.
205      *
206      * @param aFlag a <code>Boolean</code> value
207      */

208     public void setAllowMissingParamTags(boolean aFlag)
209     {
210         mAllowMissingParamTags = aFlag;
211     }
212
213     /**
214      * controls whether to allow a method which declares that it throws
215      * exceptions to omit matching throws tags in the javadoc. Defaults to
216      * false.
217      *
218      * @param aFlag a <code>Boolean</code> value
219      */

220     public void setAllowMissingThrowsTags(boolean aFlag)
221     {
222         mAllowMissingThrowsTags = aFlag;
223     }
224
225     /**
226      * controls whether to allow a method which returns non-void type to omit
227      * the return tag in the javadoc. Defaults to false.
228      *
229      * @param aFlag a <code>Boolean</code> value
230      */

231     public void setAllowMissingReturnTag(boolean aFlag)
232     {
233         mAllowMissingReturnTag = aFlag;
234     }
235
236     /**
237      * Controls whether to ignore errors when there is no javadoc. Defaults to
238      * false.
239      *
240      * @param aFlag a <code>Boolean</code> value
241      */

242     public void setAllowMissingJavadoc(boolean aFlag)
243     {
244         mAllowMissingJavadoc = aFlag;
245     }
246
247     /**
248      * Controls whether to ignore errors when there is no javadoc for a
249      * property accessor (setter/getter methods). Defaults to false.
250      *
251      * @param aFlag a <code>Boolean</code> value
252      */

253     public void setAllowMissingPropertyJavadoc(final boolean aFlag)
254     {
255         mAllowMissingPropertyJavadoc = aFlag;
256     }
257
258     /** {@inheritDoc} */
259     public int[] getDefaultTokens()
260     {
261         return new int[] {TokenTypes.PACKAGE_DEF, TokenTypes.IMPORT,
262                           TokenTypes.CLASS_DEF, TokenTypes.ENUM_DEF,
263                           TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF,
264                           TokenTypes.ANNOTATION_FIELD_DEF,
265         };
266     }
267
268     /** {@inheritDoc} */
269     public int[] getAcceptableTokens()
270     {
271         return new int[] {TokenTypes.METHOD_DEF, TokenTypes.CTOR_DEF,
272                           TokenTypes.ANNOTATION_FIELD_DEF,
273         };
274     }
275
276     /**
277      * Checks Javadoc comments for a method or constructor.
278      *
279      * @param aAST the tree node for the method or constructor.
280      */

281     protected final void processAST(DetailAST aAST)
282     {
283         final Scope theScope = calculateScope(aAST);
284         if (shouldCheck(aAST, theScope)) {
285             final FileContents contents = getFileContents();
286             final TextBlock cmt = contents.getJavadocBefore(aAST.getLineNo());
287
288             if (cmt == null) {
289                 if (!isMissingJavadocAllowed(aAST)) {
290                     log(aAST, "javadoc.missing");
291                 }
292             }
293             else {
294                 checkComment(aAST, cmt, theScope);
295             }
296         }
297     }
298
299     /**
300      * Logs error if unable to load class information.
301      *
302      * @param aIdent class name for which we can no load class.
303      */

304     protected final void logLoadError(Token aIdent)
305     {
306         logLoadErrorImpl(aIdent.getLineNo(), aIdent.getColumnNo(),
307                          "javadoc.classInfo",
308                          new Object JavaDoc[] {"@throws", aIdent.getText()});
309     }
310
311     /**
312      * The JavadocMethodCheck is about to report a missing Javadoc.
313      * This hook can be used by derived classes to allow a missing javadoc
314      * in some situations. The default implementation checks
315      * <code>allowMissingJavadoc</code> and
316      * <code>allowMissingPropertyJavadoc</code> properties, do not forget
317      * to call <code>super.isMissingJavadocAllowed(aAST)</code> in case
318      * you want to keep this logic.
319      * @param aAST the tree node for the method or constructor.
320      * @return True if this method or constructor doesn't need Javadoc.
321      */

322     protected boolean isMissingJavadocAllowed(final DetailAST aAST)
323     {
324         return mAllowMissingJavadoc
325             || (mAllowMissingPropertyJavadoc
326                 && (isSetterMethod(aAST) || isGetterMethod(aAST)));
327     }
328
329     /**
330      * Whether we should check this node.
331      *
332      * @param aAST a given node.
333      * @param aScope the scope of the node.
334      * @return whether we should check a given node.
335      */

336     private boolean shouldCheck(final DetailAST aAST, final Scope aScope)
337     {
338         final Scope surroundingScope = ScopeUtils.getSurroundingScope(aAST);
339
340         return aScope.isIn(mScope)
341                 && surroundingScope.isIn(mScope)
342                 && ((mExcludeScope == null) || !aScope.isIn(mExcludeScope)
343                     || !surroundingScope.isIn(mExcludeScope));
344     }
345
346     /**
347      * Checks the Javadoc for a method.
348      *
349      * @param aAST the token for the method
350      * @param aComment the Javadoc comment
351      * @param aScope the scope of the method.
352      */

353     private void checkComment(DetailAST aAST, TextBlock aComment, Scope aScope)
354     {
355         final List JavaDoc tags = getMethodTags(aComment);
356
357         if (hasShortCircuitTag(aAST, tags, aScope)) {
358             return;
359         }
360
361         Iterator JavaDoc it = tags.iterator();
362         if (aAST.getType() != TokenTypes.ANNOTATION_FIELD_DEF) {
363             // Check for inheritDoc
364
boolean hasInheritDocTag = false;
365             while (it.hasNext() && !hasInheritDocTag) {
366                 hasInheritDocTag |= ((JavadocTag) it.next()).isInheritDocTag();
367             }
368
369             checkParamTags(tags, aAST, !hasInheritDocTag);
370             checkThrowsTags(tags, getThrows(aAST), !hasInheritDocTag);
371             if (isFunction(aAST)) {
372                 checkReturnTag(tags, aAST.getLineNo(), !hasInheritDocTag);
373             }
374         }
375
376         // Dump out all unused tags
377
it = tags.iterator();
378         while (it.hasNext()) {
379             final JavadocTag jt = (JavadocTag) it.next();
380             if (!jt.isSeeOrInheritDocTag()) {
381                 log(jt.getLineNo(), "javadoc.unusedTagGeneral");
382             }
383         }
384     }
385
386     /**
387      * Validates whether the Javadoc has a short circuit tag. Currently this is
388      * the inheritTag. Any errors are logged.
389      *
390      * @param aAST the construct being checked
391      * @param aTags the list of Javadoc tags associated with the construct
392      * @param aScope the scope of the construct
393      * @return true if the construct has a short circuit tag.
394      */

395     private boolean hasShortCircuitTag(final DetailAST aAST, final List JavaDoc aTags,
396         final Scope aScope)
397     {
398         // Check if it contains {@inheritDoc} tag
399
if ((aTags.size() != 1)
400                 || !((JavadocTag) aTags.get(0)).isInheritDocTag())
401         {
402             return false;
403         }
404
405         // Invalid if private or a constructor
406
if ((aAST.getType() == TokenTypes.CTOR_DEF)
407                 || (aScope == Scope.PRIVATE))
408         {
409             log(aAST, "javadoc.invalidInheritDoc");
410         }
411
412         return true;
413     }
414
415     /**
416      * Returns the scope for the method/constructor at the specified AST. If
417      * the method is in an interface or annotation block, the scope is assumed
418      * to be public.
419      *
420      * @param aAST the token of the method/constructor
421      * @return the scope of the method/constructor
422      */

423     private Scope calculateScope(final DetailAST aAST)
424     {
425         final DetailAST mods = aAST.findFirstToken(TokenTypes.MODIFIERS);
426         final Scope declaredScope = ScopeUtils.getScopeFromMods(mods);
427         return ScopeUtils.inInterfaceOrAnnotationBlock(aAST) ? Scope.PUBLIC
428                 : declaredScope;
429     }
430
431     /**
432      * Returns the tags in a javadoc comment. Only finds throws, exception,
433      * param, return and see tags.
434      *
435      * @return the tags found
436      * @param aComment the Javadoc comment
437      */

438     private List JavaDoc getMethodTags(TextBlock aComment)
439     {
440         final String JavaDoc[] lines = aComment.getText();
441         final List JavaDoc tags = new ArrayList JavaDoc();
442         int currentLine = aComment.getStartLineNo() - 1;
443
444         for (int i = 0; i < lines.length; i++) {
445             currentLine++;
446             final Matcher JavaDoc javadocArgMatcher =
447                 MATCH_JAVADOC_ARG.matcher(lines[i]);
448             final Matcher JavaDoc javadocNoargMatcher =
449                 MATCH_JAVADOC_NOARG.matcher(lines[i]);
450             final Matcher JavaDoc noargCurlyMatcher =
451                 MATCH_JAVADOC_NOARG_CURLY.matcher(lines[i]);
452             final Matcher JavaDoc argMultilineStart =
453                 MATCH_JAVADOC_ARG_MULTILINE_START.matcher(lines[i]);
454             final Matcher JavaDoc noargMultilineStart =
455                 MATCH_JAVADOC_NOARG_MULTILINE_START.matcher(lines[i]);
456
457             if (javadocArgMatcher.find()) {
458                 int col = javadocArgMatcher.start(1) - 1;
459                 if (i == 0) {
460                     col += aComment.getStartColNo();
461                 }
462                 tags.add(new JavadocTag(currentLine, col, javadocArgMatcher
463                         .group(1), javadocArgMatcher.group(2)));
464             }
465             else if (javadocNoargMatcher.find()) {
466                 int col = javadocNoargMatcher.start(1) - 1;
467                 if (i == 0) {
468                     col += aComment.getStartColNo();
469                 }
470                 tags.add(new JavadocTag(currentLine, col, javadocNoargMatcher
471                         .group(1)));
472             }
473             else if (noargCurlyMatcher.find()) {
474                 int col = noargCurlyMatcher.start(1) - 1;
475                 if (i == 0) {
476                     col += aComment.getStartColNo();
477                 }
478                 tags.add(new JavadocTag(currentLine, col, noargCurlyMatcher
479                         .group(1)));
480             }
481             else if (argMultilineStart.find()) {
482                 final String JavaDoc p1 = argMultilineStart.group(1);
483                 final String JavaDoc p2 = argMultilineStart.group(2);
484                 int col = argMultilineStart.start(1) - 1;
485                 if (i == 0) {
486                     col += aComment.getStartColNo();
487                 }
488
489                 // Look for the rest of the comment if all we saw was
490
// the tag and the name. Stop when we see '*/' (end of
491
// Javadoc), '@' (start of next tag), or anything that's
492
// not whitespace or '*' characters.
493
int remIndex = i + 1;
494                 while (remIndex < lines.length) {
495                     final Matcher JavaDoc multilineCont = MATCH_JAVADOC_MULTILINE_CONT
496                             .matcher(lines[remIndex]);
497                     if (multilineCont.find()) {
498                         remIndex = lines.length;
499                         final String JavaDoc lFin = multilineCont.group(1);
500                         if (!lFin.equals(NEXT_TAG) && !lFin.equals(END_JAVADOC))
501                         {
502                             tags.add(new JavadocTag(currentLine, col, p1, p2));
503                         }
504                     }
505                     remIndex++;
506                 }
507             }
508             else if (noargMultilineStart.find()) {
509                 final String JavaDoc p1 = noargMultilineStart.group(1);
510                 int col = noargMultilineStart.start(1) - 1;
511                 if (i == 0) {
512                     col += aComment.getStartColNo();
513                 }
514
515                 // Look for the rest of the comment if all we saw was
516
// the tag and the name. Stop when we see '*/' (end of
517
// Javadoc), '@' (start of next tag), or anything that's
518
// not whitespace or '*' characters.
519
int remIndex = i + 1;
520                 while (remIndex < lines.length) {
521                     final Matcher JavaDoc multilineCont = MATCH_JAVADOC_MULTILINE_CONT
522                             .matcher(lines[remIndex]);
523                     if (multilineCont.find()) {
524                         remIndex = lines.length;
525                         final String JavaDoc lFin = multilineCont.group(1);
526                         if (!lFin.equals(NEXT_TAG) && !lFin.equals(END_JAVADOC))
527                         {
528                             tags.add(new JavadocTag(currentLine, col, p1));
529                         }
530                     }
531                     remIndex++;
532                 }
533             }
534         }
535         return tags;
536     }
537
538     /**
539      * Computes the parameter nodes for a method.
540      *
541      * @param aAST the method node.
542      * @return the list of parameter nodes for aAST.
543      */

544     private List JavaDoc getParameters(DetailAST aAST)
545     {
546         final DetailAST params = aAST.findFirstToken(TokenTypes.PARAMETERS);
547         final List JavaDoc retVal = new ArrayList JavaDoc();
548
549         DetailAST child = (DetailAST) params.getFirstChild();
550         while (child != null) {
551             if (child.getType() == TokenTypes.PARAMETER_DEF) {
552                 final DetailAST ident = child.findFirstToken(TokenTypes.IDENT);
553                 retVal.add(ident);
554             }
555             child = (DetailAST) child.getNextSibling();
556         }
557         return retVal;
558     }
559
560     /**
561      * Computes the exception nodes for a method.
562      *
563      * @param aAST the method node.
564      * @return the list of exception nodes for aAST.
565      */

566     private List JavaDoc getThrows(DetailAST aAST)
567     {
568         final List JavaDoc retVal = new ArrayList JavaDoc();
569         final DetailAST throwsAST = aAST
570                 .findFirstToken(TokenTypes.LITERAL_THROWS);
571         if (throwsAST != null) {
572             DetailAST child = (DetailAST) throwsAST.getFirstChild();
573             while (child != null) {
574                 if ((child.getType() == TokenTypes.IDENT)
575                         || (child.getType() == TokenTypes.DOT))
576                 {
577                     final FullIdent fi = FullIdent.createFullIdent(child);
578                     final ExceptionInfo ei = new ExceptionInfo(new Token(fi),
579                             getCurrentClassName());
580                     retVal.add(ei);
581                 }
582                 child = (DetailAST) child.getNextSibling();
583             }
584         }
585         return retVal;
586     }
587
588     /**
589      * Checks a set of tags for matching parameters.
590      *
591      * @param aTags the tags to check
592      * @param aParent the node which takes the parameters
593      * @param aReportExpectedTags whether we should report if do not find
594      * expected tag
595      */

596     private void checkParamTags(final List JavaDoc aTags, final DetailAST aParent,
597         boolean aReportExpectedTags)
598     {
599         final List JavaDoc params = getParameters(aParent);
600         final List JavaDoc typeParams = CheckUtils.getTypeParameters(aParent);
601
602         // Loop over the tags, checking to see they exist in the params.
603
final ListIterator JavaDoc tagIt = aTags.listIterator();
604         while (tagIt.hasNext()) {
605             final JavadocTag tag = (JavadocTag) tagIt.next();
606
607             if (!tag.isParamTag()) {
608                 continue;
609             }
610
611             tagIt.remove();
612
613             boolean found = false;
614
615             // Loop looking for matching param
616
final Iterator JavaDoc paramIt = params.iterator();
617             while (paramIt.hasNext()) {
618                 final DetailAST param = (DetailAST) paramIt.next();
619                 if (param.getText().equals(tag.getArg1())) {
620                     found = true;
621                     paramIt.remove();
622                     break;
623                 }
624             }
625
626             if (tag.getArg1().startsWith("<") && tag.getArg1().endsWith(">")) {
627                 // Loop looking for matching type param
628
final Iterator JavaDoc typeParamsIt = typeParams.iterator();
629                 while (typeParamsIt.hasNext()) {
630                     final DetailAST typeParam = (DetailAST) typeParamsIt.next();
631                     if (typeParam.findFirstToken(TokenTypes.IDENT).getText()
632                             .equals(
633                                     tag.getArg1().substring(1,
634                                             tag.getArg1().length() - 1)))
635                     {
636                         found = true;
637                         typeParamsIt.remove();
638                         break;
639                     }
640                 }
641
642             }
643
644             // Handle extra JavadocTag
645
if (!found) {
646                 log(tag.getLineNo(), tag.getColumnNo(), "javadoc.unusedTag",
647                         "@param", tag.getArg1());
648             }
649         }
650
651         // Now dump out all type parameters/parameters without tags :- unless
652
// the user has chosen to suppress these problems
653
if (!mAllowMissingParamTags && aReportExpectedTags) {
654             final Iterator JavaDoc paramIt = params.iterator();
655             while (paramIt.hasNext()) {
656                 final DetailAST param = (DetailAST) paramIt.next();
657                 log(param, "javadoc.expectedTag", "@param", param.getText());
658             }
659
660             final Iterator JavaDoc typeParamsIt = typeParams.iterator();
661             while (typeParamsIt.hasNext()) {
662                 final DetailAST typeParam = (DetailAST) typeParamsIt.next();
663                 log(typeParam, "javadoc.expectedTag", "@param", "<"
664                         + typeParam.findFirstToken(TokenTypes.IDENT).getText()
665                         + ">");
666             }
667         }
668     }
669
670     /**
671      * Checks whether a method is a function.
672      *
673      * @param aAST the method node.
674      * @return whether the method is a function.
675      */

676     private boolean isFunction(DetailAST aAST)
677     {
678         boolean retVal = false;
679         if (aAST.getType() == TokenTypes.METHOD_DEF) {
680             final DetailAST typeAST = aAST.findFirstToken(TokenTypes.TYPE);
681             if ((typeAST != null)
682                 && (typeAST.findFirstToken(TokenTypes.LITERAL_VOID) == null))
683             {
684                 retVal = true;
685             }
686         }
687         return retVal;
688     }
689
690     /**
691      * Checks for only one return tag. All return tags will be removed from the
692      * supplied list.
693      *
694      * @param aTags the tags to check
695      * @param aLineNo the line number of the expected tag
696      * @param aReportExpectedTags whether we should report if do not find
697      * expected tag
698      */

699     private void checkReturnTag(List JavaDoc aTags, int aLineNo,
700         boolean aReportExpectedTags)
701     {
702         // Loop over tags finding return tags. After the first one, report an
703
// error.
704
boolean found = false;
705         final ListIterator JavaDoc it = aTags.listIterator();
706         while (it.hasNext()) {
707             final JavadocTag jt = (JavadocTag) it.next();
708             if (jt.isReturnTag()) {
709                 if (found) {
710                     log(jt.getLineNo(), jt.getColumnNo(),
711                             "javadoc.return.duplicate");
712                 }
713                 found = true;
714                 it.remove();
715             }
716         }
717
718         // Handle there being no @return tags :- unless
719
// the user has chosen to suppress these problems
720
if (!found && !mAllowMissingReturnTag && aReportExpectedTags) {
721             log(aLineNo, "javadoc.return.expected");
722         }
723     }
724
725     /**
726      * Checks a set of tags for matching throws.
727      *
728      * @param aTags the tags to check
729      * @param aThrows the throws to check
730      * @param aReportExpectedTags whether we should report if do not find
731      * expected tag
732      */

733     private void checkThrowsTags(List JavaDoc aTags, List JavaDoc aThrows,
734         boolean aReportExpectedTags)
735     {
736         // Loop over the tags, checking to see they exist in the throws.
737
final Set JavaDoc foundThrows = new HashSet JavaDoc(); //used for performance only
738
final ListIterator JavaDoc tagIt = aTags.listIterator();
739         while (tagIt.hasNext()) {
740             final JavadocTag tag = (JavadocTag) tagIt.next();
741
742             if (!tag.isThrowsTag()) {
743                 continue;
744             }
745
746             tagIt.remove();
747
748             // Loop looking for matching throw
749
final String JavaDoc documentedEx = tag.getArg1();
750             final Token token = new Token(tag.getArg1(), tag.getLineNo(), tag
751                     .getColumnNo());
752             final ClassInfo documentedCI = createClassInfo(token,
753                     getCurrentClassName());
754             boolean found = foundThrows.contains(documentedEx);
755
756             final ListIterator JavaDoc throwIt = aThrows.listIterator();
757             while (!found && throwIt.hasNext()) {
758                 final ExceptionInfo ei = (ExceptionInfo) throwIt.next();
759
760                 if (documentedCI.getClazz() == ei.getClazz()) {
761                     found = true;
762                     ei.setFound();
763                     foundThrows.add(documentedEx);
764                 }
765                 else if (mAllowThrowsTagsForSubclasses) {
766                     found = isSubclass(documentedCI.getClazz(), ei.getClazz());
767                 }
768             }
769
770             // Handle extra JavadocTag.
771
if (!found) {
772                 boolean reqd = true;
773                 if (mAllowUndeclaredRTE) {
774                     reqd = !isUnchecked(documentedCI.getClazz());
775                 }
776
777                 if (reqd) {
778                     log(tag.getLineNo(), tag.getColumnNo(),
779                             "javadoc.unusedTag", "@throws", tag.getArg1());
780
781                 }
782             }
783         }
784
785         // Now dump out all throws without tags :- unless
786
// the user has chosen to suppress these problems
787
if (!mAllowMissingThrowsTags && aReportExpectedTags) {
788             final ListIterator JavaDoc throwIt = aThrows.listIterator();
789             while (throwIt.hasNext()) {
790                 final ExceptionInfo ei = (ExceptionInfo) throwIt.next();
791                 if (!ei.isFound()) {
792                     final Token fi = ei.getName();
793                     log(fi.getLineNo(), fi.getColumnNo(),
794                             "javadoc.expectedTag", "@throws", fi.getText());
795                 }
796             }
797         }
798     }
799
800     /**
801      * Returns whether an AST represents a setter method.
802      * @param aAST the AST to check with
803      * @return whether the AST represents a setter method
804      */

805     private boolean isSetterMethod(final DetailAST aAST)
806     {
807         // Check have a method with exactly 7 children which are all that
808
// is allowed in a proper setter method which does not throw any
809
// exceptions.
810
if ((aAST.getType() != TokenTypes.METHOD_DEF)
811                 || (aAST.getChildCount() != MAX_CHILDREN))
812         {
813             return false;
814         }
815
816         // Should I handle only being in a class????
817

818         // Check the name matches format setX...
819
final DetailAST type = aAST.findFirstToken(TokenTypes.TYPE);
820         final String JavaDoc name = type.getNextSibling().getText();
821         if (!name.matches("^set[A-Z].*")) { // Depends on JDK 1.4
822
return false;
823         }
824
825         // Check the return type is void
826
if (type.getChildCount(TokenTypes.LITERAL_VOID) == 0) {
827             return false;
828         }
829
830         // Check that is had only one parameter
831
final DetailAST params = aAST.findFirstToken(TokenTypes.PARAMETERS);
832         if ((params == null)
833                 || (params.getChildCount(TokenTypes.PARAMETER_DEF) != 1))
834         {
835             return false;
836         }
837
838         // Now verify that the body consists of:
839
// SLIST -> EXPR -> ASSIGN
840
// SEMI
841
// RCURLY
842
final DetailAST slist = aAST.findFirstToken(TokenTypes.SLIST);
843         if ((slist == null) || (slist.getChildCount() != BODY_SIZE)) {
844             return false;
845         }
846
847         final AST expr = slist.getFirstChild();
848         if ((expr.getType() != TokenTypes.EXPR)
849                 || (expr.getFirstChild().getType() != TokenTypes.ASSIGN))
850         {
851             return false;
852         }
853
854         return true;
855     }
856
857     /**
858      * Returns whether an AST represents a getter method.
859      * @param aAST the AST to check with
860      * @return whether the AST represents a getter method
861      */

862     private boolean isGetterMethod(final DetailAST aAST)
863     {
864         // Check have a method with exactly 7 children which are all that
865
// is allowed in a proper getter method which does not throw any
866
// exceptions.
867
if ((aAST.getType() != TokenTypes.METHOD_DEF)
868                 || (aAST.getChildCount() != MAX_CHILDREN))
869         {
870             return false;
871         }
872
873         // Check the name matches format of getX or isX. Technically I should
874
// check that the format isX is only used with a boolean type.
875
final DetailAST type = aAST.findFirstToken(TokenTypes.TYPE);
876         final String JavaDoc name = type.getNextSibling().getText();
877         if (!name.matches("^(is|get)[A-Z].*")) { // Depends on JDK 1.4
878
return false;
879         }
880
881         // Check the return type is void
882
if (type.getChildCount(TokenTypes.LITERAL_VOID) > 0) {
883             return false;
884         }
885
886         // Check that is had only one parameter
887
final DetailAST params = aAST.findFirstToken(TokenTypes.PARAMETERS);
888         if ((params == null)
889                 || (params.getChildCount(TokenTypes.PARAMETER_DEF) > 0))
890         {
891             return false;
892         }
893
894         // Now verify that the body consists of:
895
// SLIST -> RETURN
896
// RCURLY
897
final DetailAST slist = aAST.findFirstToken(TokenTypes.SLIST);
898         if ((slist == null) || (slist.getChildCount() != 2)) {
899             return false;
900         }
901
902         final AST expr = slist.getFirstChild();
903         if ((expr.getType() != TokenTypes.LITERAL_RETURN)
904                 || (expr.getFirstChild().getType() != TokenTypes.EXPR))
905         {
906             return false;
907         }
908
909         return true;
910     }
911
912     /** Stores useful information about declared exception. */
913     private class ExceptionInfo
914     {
915         /** does the exception have throws tag associated with. */
916         private boolean mFound;
917         /** class information associated with this exception. */
918         private ClassInfo mClassInfo;
919
920         /**
921          * Creates new instance for <code>FullIdent</code>.
922          *
923          * @param aIdent the exception
924          * @param aCurrentClass name of current class.
925          */

926         ExceptionInfo(Token aIdent, String JavaDoc aCurrentClass)
927         {
928             mClassInfo = createClassInfo(aIdent, aCurrentClass);
929         }
930
931         /** Mark that the exception has associated throws tag */
932         final void setFound()
933         {
934             mFound = true;
935         }
936
937         /** @return whether the exception has throws tag associated with */
938         final boolean isFound()
939         {
940             return mFound;
941         }
942
943         /** @return exception's name */
944         final Token getName()
945         {
946             return mClassInfo.getName();
947         }
948
949         /** @return class for this exception */
950         final Class JavaDoc getClazz()
951         {
952             return mClassInfo.getClazz();
953         }
954     }
955 }
956
Popular Tags