Coverage Report - com.puppycrawl.tools.checkstyle.checks.annotation.MissingDeprecatedCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
MissingDeprecatedCheck
97%
41/42
92%
26/28
4.5
 
 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.annotation;
 20  
 
 21  
 import java.util.regex.Matcher;
 22  
 import java.util.regex.Pattern;
 23  
 
 24  
 import com.puppycrawl.tools.checkstyle.api.AnnotationUtility;
 25  
 import com.puppycrawl.tools.checkstyle.api.Check;
 26  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 27  
 import com.puppycrawl.tools.checkstyle.api.JavadocTagInfo;
 28  
 import com.puppycrawl.tools.checkstyle.api.TextBlock;
 29  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 30  
 import com.puppycrawl.tools.checkstyle.api.Utils;
 31  
 
 32  
 /**
 33  
  * <p>
 34  
  * This class is used to verify that both the
 35  
  * {@link java.lang.Deprecated Deprecated} annotation
 36  
  * and the deprecated javadoc tag are present when
 37  
  * either one is present.
 38  
  * </p>
 39  
  *
 40  
  * <p>
 41  
  * Both ways of flagging deprecation serve their own purpose.  The
 42  
  * {@link java.lang.Deprecated Deprecated} annotation is used for
 43  
  * compilers and development tools.  The deprecated javadoc tag is
 44  
  * used to document why something is deprecated and what, if any,
 45  
  * alternatives exist.
 46  
  * </p>
 47  
  *
 48  
  * <p>
 49  
  * In order to properly mark something as deprecated both forms of
 50  
  * deprecation should be present.
 51  
  * </p>
 52  
  *
 53  
  * <p>
 54  
  * Package deprecation is a exception to the rule of always using the
 55  
  * javadoc tag and annotation to deprecate.  Only the package-info.java
 56  
  * file can contain a Deprecated annotation and it CANNOT contain
 57  
  * a deprecated javadoc tag.  This is the case with
 58  
  * Sun's javadoc tool released with JDK 1.6.0_11.  As a result, this check
 59  
  * does not deal with Deprecated packages in any way.  <b>No official
 60  
  * documentation was found confirming this behavior is correct
 61  
  * (of the javadoc tool).</b>
 62  
  * </p>
 63  
  *
 64  
  * <p>
 65  
  * To configure this check do the following:
 66  
  * </p>
 67  
  *
 68  
  * <pre>
 69  
  * &lt;module name="JavadocDeprecated"/&gt;
 70  
  * </pre>
 71  
  *
 72  
  * @author Travis Schneeberger
 73  
  */
 74  4
 public final class MissingDeprecatedCheck extends Check
 75  
 {
 76  
     /** {@link Deprecated Deprecated} annotation name */
 77  
     private static final String DEPRECATED = "Deprecated";
 78  
 
 79  
     /** fully-qualified {@link Deprecated Deprecated} annotation name */
 80  
     private static final String FQ_DEPRECATED = "java.lang." + DEPRECATED;
 81  
 
 82  
     /** compiled regexp to match Javadoc tag with no argument * */
 83  1
     private static final Pattern MATCH_DEPRECATED =
 84  
         Utils.createPattern("@(deprecated)\\s+\\S");
 85  
 
 86  
     /** compiled regexp to match first part of multilineJavadoc tags * */
 87  1
     private static final Pattern MATCH_DEPRECATED_MULTILINE_START =
 88  
         Utils.createPattern("@(deprecated)\\s*$");
 89  
 
 90  
     /** compiled regexp to look for a continuation of the comment * */
 91  1
     private static final Pattern MATCH_DEPRECATED_MULTILINE_CONT =
 92  
         Utils.createPattern("(\\*/|@|[^\\s\\*])");
 93  
 
 94  
     /** Multiline finished at end of comment * */
 95  
     private static final String END_JAVADOC = "*/";
 96  
     /** Multiline finished at next Javadoc * */
 97  
     private static final String NEXT_TAG = "@";
 98  
 
 99  
     /** {@inheritDoc} */
 100  
     @Override
 101  
     public int[] getDefaultTokens()
 102  
     {
 103  4
         return this.getAcceptableTokens();
 104  
     }
 105  
 
 106  
     /** {@inheritDoc} */
 107  
     @Override
 108  
     public int[] getAcceptableTokens()
 109  
     {
 110  4
         return new int[] {
 111  
             TokenTypes.INTERFACE_DEF,
 112  
             TokenTypes.CLASS_DEF,
 113  
             TokenTypes.ANNOTATION_DEF,
 114  
             TokenTypes.ENUM_DEF,
 115  
             TokenTypes.METHOD_DEF,
 116  
             TokenTypes.CTOR_DEF,
 117  
             TokenTypes.VARIABLE_DEF,
 118  
             TokenTypes.ENUM_CONSTANT_DEF,
 119  
             TokenTypes.ANNOTATION_FIELD_DEF,
 120  
         };
 121  
     }
 122  
 
 123  
     /** {@inheritDoc} */
 124  
     @Override
 125  
     public void visitToken(final DetailAST aAST)
 126  
     {
 127  41
         final TextBlock javadoc =
 128  
             this.getFileContents().getJavadocBefore(aAST.getLineNo());
 129  
 
 130  41
         final boolean containsAnnotation =
 131  
             AnnotationUtility.containsAnnotation(aAST, DEPRECATED)
 132  
             || AnnotationUtility.containsAnnotation(aAST, FQ_DEPRECATED);
 133  
 
 134  41
         final boolean containsJavadocTag = this.containsJavadocTag(javadoc);
 135  
 
 136  41
         if (containsAnnotation ^ containsJavadocTag) {
 137  20
             this.log(aAST.getLineNo(), "annotation.missing.deprecated");
 138  
         }
 139  41
     }
 140  
 
 141  
     /**
 142  
      * Checks to see if the text block contains a deprecated tag.
 143  
      *
 144  
      * @param aJavadoc the javadoc of the AST
 145  
      * @return true if contains the tag
 146  
      */
 147  
     private boolean containsJavadocTag(final TextBlock aJavadoc)
 148  
     {
 149  41
         if (aJavadoc == null) {
 150  7
             return false;
 151  
         }
 152  
 
 153  34
         final String[] lines = aJavadoc.getText();
 154  
 
 155  34
         boolean found = false;
 156  
 
 157  34
         int currentLine = aJavadoc.getStartLineNo() - 1;
 158  
 
 159  145
         for (int i = 0; i < lines.length; i++) {
 160  111
             currentLine++;
 161  111
             final String line = lines[i];
 162  
 
 163  111
             final Matcher javadocNoargMatcher =
 164  
                 MissingDeprecatedCheck.MATCH_DEPRECATED.matcher(line);
 165  111
             final Matcher noargMultilineStart =
 166  
                 MissingDeprecatedCheck.
 167  
                     MATCH_DEPRECATED_MULTILINE_START.matcher(line);
 168  
 
 169  111
             if (javadocNoargMatcher.find()) {
 170  24
                 if (found) {
 171  3
                     this.log(currentLine, "javadoc.duplicateTag",
 172  
                         JavadocTagInfo.DEPRECATED.getText());
 173  
                 }
 174  24
                 found = true;
 175  
             }
 176  87
             else if (noargMultilineStart.find()) {
 177  
                 // Look for the rest of the comment if all we saw was
 178  
                 // the tag and the name. Stop when we see '*/' (end of
 179  
                 // Javadoc), '@' (start of next tag), or anything that's
 180  
                 // not whitespace or '*' characters.
 181  
 
 182  9
                 for (int remIndex = i + 1;
 183  27
                     remIndex < lines.length; remIndex++)
 184  
                 {
 185  9
                     final Matcher multilineCont =
 186  
                         MissingDeprecatedCheck.MATCH_DEPRECATED_MULTILINE_CONT
 187  
                         .matcher(lines[remIndex]);
 188  
 
 189  9
                     if (multilineCont.find()) {
 190  9
                         remIndex = lines.length;
 191  9
                         final String lFin = multilineCont.group(1);
 192  9
                         if (!lFin.equals(MissingDeprecatedCheck.NEXT_TAG)
 193  
                             && !lFin.equals(MissingDeprecatedCheck.END_JAVADOC))
 194  
                         {
 195  3
                             if (found) {
 196  0
                                 this.log(currentLine, "javadoc.duplicateTag",
 197  
                                     JavadocTagInfo.DEPRECATED.getText());
 198  
                             }
 199  3
                             found = true;
 200  
                         }
 201  
                         else {
 202  6
                             this.log(currentLine, "javadoc.missing");
 203  6
                             if (found) {
 204  2
                                 this.log(currentLine, "javadoc.duplicateTag",
 205  
                                     JavadocTagInfo.DEPRECATED.getText());
 206  
                             }
 207  6
                             found = true;
 208  
                         }
 209  
                     }
 210  
                 }
 211  
             }
 212  
         }
 213  34
         return found;
 214  
     }
 215  
 }