Coverage Report - com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
JavadocTypeCheck
92%
78/84
95%
57/60
3.615
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  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 com.puppycrawl.tools.checkstyle.api.Check;
 22  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 23  
 import com.puppycrawl.tools.checkstyle.api.FileContents;
 24  
 import com.puppycrawl.tools.checkstyle.api.JavadocTagInfo;
 25  
 import com.puppycrawl.tools.checkstyle.api.Scope;
 26  
 import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
 27  
 import com.puppycrawl.tools.checkstyle.api.TextBlock;
 28  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 29  
 import com.puppycrawl.tools.checkstyle.api.Utils;
 30  
 import com.puppycrawl.tools.checkstyle.checks.CheckUtils;
 31  
 import java.util.List;
 32  
 import java.util.regex.Matcher;
 33  
 import java.util.regex.Pattern;
 34  
 import java.util.regex.PatternSyntaxException;
 35  
 import org.apache.commons.beanutils.ConversionException;
 36  
 
 37  
 /**
 38  
  * Checks the Javadoc of a type.
 39  
  *
 40  
  * @author Oliver Burn
 41  
  * @author Michael Tamm
 42  
  */
 43  21
 public class JavadocTypeCheck
 44  
     extends Check
 45  
 {
 46  
     /** the scope to check for */
 47  21
     private Scope mScope = Scope.PRIVATE;
 48  
     /** the visibility scope where Javadoc comments shouldn't be checked **/
 49  
     private Scope mExcludeScope;
 50  
     /** compiled regexp to match author tag content **/
 51  
     private Pattern mAuthorFormatPattern;
 52  
     /** compiled regexp to match version tag content **/
 53  
     private Pattern mVersionFormatPattern;
 54  
     /** regexp to match author tag content */
 55  
     private String mAuthorFormat;
 56  
     /** regexp to match version tag content */
 57  
     private String mVersionFormat;
 58  
     /**
 59  
      * controls whether to ignore errors when a method has type parameters but
 60  
      * does not have matching param tags in the javadoc. Defaults to false.
 61  
      */
 62  
     private boolean mAllowMissingParamTags;
 63  
     /** controls whether to flag errors for unknown tags. Defaults to false. */
 64  
     private boolean mAllowUnknownTags;
 65  
 
 66  
     /**
 67  
      * Sets the scope to check.
 68  
      * @param aFrom string to set scope from
 69  
      */
 70  
     public void setScope(String aFrom)
 71  
     {
 72  7
         mScope = Scope.getInstance(aFrom);
 73  7
     }
 74  
 
 75  
     /**
 76  
      * Set the excludeScope.
 77  
      * @param aScope a <code>String</code> value
 78  
      */
 79  
     public void setExcludeScope(String aScope)
 80  
     {
 81  1
         mExcludeScope = Scope.getInstance(aScope);
 82  1
     }
 83  
 
 84  
     /**
 85  
      * Set the author tag pattern.
 86  
      * @param aFormat a <code>String</code> value
 87  
      * @throws ConversionException unable to parse aFormat
 88  
      */
 89  
     public void setAuthorFormat(String aFormat)
 90  
         throws ConversionException
 91  
     {
 92  
         try {
 93  3
             mAuthorFormat = aFormat;
 94  3
             mAuthorFormatPattern = Utils.getPattern(aFormat);
 95  
         }
 96  0
         catch (final PatternSyntaxException e) {
 97  0
             throw new ConversionException("unable to parse " + aFormat, e);
 98  3
         }
 99  3
     }
 100  
 
 101  
     /**
 102  
      * Set the version format pattern.
 103  
      * @param aFormat a <code>String</code> value
 104  
      * @throws ConversionException unable to parse aFormat
 105  
      */
 106  
     public void setVersionFormat(String aFormat)
 107  
         throws ConversionException
 108  
     {
 109  
         try {
 110  3
             mVersionFormat = aFormat;
 111  3
             mVersionFormatPattern = Utils.getPattern(aFormat);
 112  
         }
 113  0
         catch (final PatternSyntaxException e) {
 114  0
             throw new ConversionException("unable to parse " + aFormat, e);
 115  3
         }
 116  
 
 117  3
     }
 118  
 
 119  
     /**
 120  
      * Controls whether to allow a type which has type parameters to
 121  
      * omit matching param tags in the javadoc. Defaults to false.
 122  
      *
 123  
      * @param aFlag a <code>Boolean</code> value
 124  
      */
 125  
     public void setAllowMissingParamTags(boolean aFlag)
 126  
     {
 127  1
         mAllowMissingParamTags = aFlag;
 128  1
     }
 129  
 
 130  
     /**
 131  
      * Controls whether to flag errors for unknown tags. Defaults to false.
 132  
      * @param aFlag a <code>Boolean</code> value
 133  
      */
 134  
     public void setAllowUnknownTags(boolean aFlag)
 135  
     {
 136  1
         mAllowUnknownTags = aFlag;
 137  1
     }
 138  
 
 139  
     @Override
 140  
     public int[] getDefaultTokens()
 141  
     {
 142  21
         return new int[] {
 143  
             TokenTypes.INTERFACE_DEF,
 144  
             TokenTypes.CLASS_DEF,
 145  
             TokenTypes.ENUM_DEF,
 146  
             TokenTypes.ANNOTATION_DEF,
 147  
         };
 148  
     }
 149  
 
 150  
     @Override
 151  
     public void visitToken(DetailAST aAST)
 152  
     {
 153  145
         if (shouldCheck(aAST)) {
 154  107
             final FileContents contents = getFileContents();
 155  107
             final int lineNo = aAST.getLineNo();
 156  107
             final TextBlock cmt = contents.getJavadocBefore(lineNo);
 157  107
             if (cmt == null) {
 158  39
                 log(lineNo, "javadoc.missing");
 159  
             }
 160  68
             else if (ScopeUtils.isOuterMostType(aAST)) {
 161  
                 // don't check author/version for inner classes
 162  63
                 final List<JavadocTag> tags = getJavadocTags(cmt);
 163  63
                 checkTag(lineNo, tags, JavadocTagInfo.AUTHOR.getName(),
 164  
                          mAuthorFormatPattern, mAuthorFormat);
 165  63
                 checkTag(lineNo, tags, JavadocTagInfo.VERSION.getName(),
 166  
                          mVersionFormatPattern, mVersionFormat);
 167  
 
 168  63
                 final List<String> typeParamNames =
 169  
                     CheckUtils.getTypeParameterNames(aAST);
 170  
 
 171  63
                 if (!mAllowMissingParamTags) {
 172  
                     //Check type parameters that should exist, do
 173  62
                     for (final String string : typeParamNames) {
 174  3
                         checkTypeParamTag(
 175  
                             lineNo, tags, string);
 176  
                     }
 177  
                 }
 178  
 
 179  63
                 checkUnusedTypeParamTags(tags, typeParamNames);
 180  
             }
 181  
         }
 182  145
     }
 183  
 
 184  
     /**
 185  
      * Whether we should check this node.
 186  
      * @param aAST a given node.
 187  
      * @return whether we should check a given node.
 188  
      */
 189  
     private boolean shouldCheck(final DetailAST aAST)
 190  
     {
 191  145
         final DetailAST mods = aAST.findFirstToken(TokenTypes.MODIFIERS);
 192  145
         final Scope declaredScope = ScopeUtils.getScopeFromMods(mods);
 193  145
         final Scope scope =
 194  
             ScopeUtils.inInterfaceOrAnnotationBlock(aAST)
 195  
                 ? Scope.PUBLIC : declaredScope;
 196  145
         final Scope surroundingScope = ScopeUtils.getSurroundingScope(aAST);
 197  
 
 198  145
         return scope.isIn(mScope)
 199  
             && ((surroundingScope == null) || surroundingScope.isIn(mScope))
 200  
             && ((mExcludeScope == null)
 201  
                 || !scope.isIn(mExcludeScope)
 202  
                 || ((surroundingScope != null)
 203  
                 && !surroundingScope.isIn(mExcludeScope)));
 204  
     }
 205  
 
 206  
     /**
 207  
      * Gets all standalone tags from a given javadoc.
 208  
      * @param aCmt the Javadoc comment to process.
 209  
      * @return all standalone tags from the given javadoc.
 210  
      */
 211  
     private List<JavadocTag> getJavadocTags(TextBlock aCmt)
 212  
     {
 213  63
         final JavadocTags tags = JavadocUtils.getJavadocTags(aCmt,
 214  
             JavadocUtils.JavadocTagType.BLOCK);
 215  63
         if (!mAllowUnknownTags) {
 216  62
             for (final InvalidJavadocTag tag : tags.getInvalidTags()) {
 217  1
                 log(tag.getLine(), tag.getCol(), "javadoc.unknownTag",
 218  
                     tag.getName());
 219  
             }
 220  
         }
 221  63
         return tags.getValidTags();
 222  
     }
 223  
 
 224  
     /**
 225  
      * Verifies that a type definition has a required tag.
 226  
      * @param aLineNo the line number for the type definition.
 227  
      * @param aTags tags from the Javadoc comment for the type definition.
 228  
      * @param aTag the required tag name.
 229  
      * @param aFormatPattern regexp for the tag value.
 230  
      * @param aFormat pattern for the tag value.
 231  
      */
 232  
     private void checkTag(int aLineNo, List<JavadocTag> aTags, String aTag,
 233  
                           Pattern aFormatPattern, String aFormat)
 234  
     {
 235  126
         if (aFormatPattern == null) {
 236  72
             return;
 237  
         }
 238  
 
 239  54
         int tagCount = 0;
 240  134
         for (int i = aTags.size() - 1; i >= 0; i--) {
 241  80
             final JavadocTag tag = aTags.get(i);
 242  80
             if (tag.getTagName().equals(aTag)) {
 243  40
                 tagCount++;
 244  40
                 if (!aFormatPattern.matcher(tag.getArg1()).find()) {
 245  15
                     log(aLineNo, "type.tagFormat", "@" + aTag, aFormat);
 246  
                 }
 247  
             }
 248  
         }
 249  54
         if (tagCount == 0) {
 250  14
             log(aLineNo, "type.missingTag", "@" + aTag);
 251  
         }
 252  54
     }
 253  
 
 254  
     /**
 255  
      * Verifies that a type definition has the specified param tag for
 256  
      * the specified type parameter name.
 257  
      * @param aLineNo the line number for the type definition.
 258  
      * @param aTags tags from the Javadoc comment for the type definition.
 259  
      * @param aTypeParamName the name of the type parameter
 260  
      */
 261  
     private void checkTypeParamTag(final int aLineNo,
 262  
             final List<JavadocTag> aTags, final String aTypeParamName)
 263  
     {
 264  3
         boolean found = false;
 265  18
         for (int i = aTags.size() - 1; i >= 0; i--) {
 266  15
             final JavadocTag tag = aTags.get(i);
 267  15
             if (tag.isParamTag()
 268  
                 && (tag.getArg1() != null)
 269  
                 && (tag.getArg1().indexOf("<" + aTypeParamName + ">") == 0))
 270  
             {
 271  2
                 found = true;
 272  
             }
 273  
         }
 274  3
         if (!found) {
 275  1
             log(aLineNo, "type.missingTag",
 276  
                 JavadocTagInfo.PARAM.getText() + " <" + aTypeParamName + ">");
 277  
         }
 278  3
     }
 279  
 
 280  
     /**
 281  
      * Checks for unused param tags for type parameters.
 282  
      * @param aTags tags from the Javadoc comment for the type definition.
 283  
      * @param aTypeParamNames names of type parameters
 284  
      */
 285  
     private void checkUnusedTypeParamTags(
 286  
         final List<JavadocTag> aTags,
 287  
         final List<String> aTypeParamNames)
 288  
     {
 289  63
         final Pattern pattern = Utils.getPattern("\\s*<([^>]+)>.*");
 290  156
         for (int i = aTags.size() - 1; i >= 0; i--) {
 291  93
             final JavadocTag tag = aTags.get(i);
 292  93
             if (tag.isParamTag()) {
 293  
 
 294  6
                 if (tag.getArg1() != null) {
 295  
 
 296  6
                     final Matcher matcher = pattern.matcher(tag.getArg1());
 297  6
                     String typeParamName = null;
 298  
 
 299  6
                     if (matcher.matches()) {
 300  6
                         typeParamName = matcher.group(1).trim();
 301  6
                         if (!aTypeParamNames.contains(typeParamName)) {
 302  2
                             log(tag.getLineNo(), tag.getColumnNo(),
 303  
                                 "javadoc.unusedTag",
 304  
                                 JavadocTagInfo.PARAM.getText(),
 305  
                                 "<" + typeParamName + ">");
 306  
                         }
 307  
                     }
 308  
                     else {
 309  0
                         log(tag.getLineNo(), tag.getColumnNo(),
 310  
                             "javadoc.unusedTagGeneral");
 311  
                     }
 312  6
                 }
 313  
                 else {
 314  0
                     log(tag.getLineNo(), tag.getColumnNo(),
 315  
                         "javadoc.unusedTagGeneral");
 316  
                 }
 317  
             }
 318  
         }
 319  63
     }
 320  
 }