1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
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  
20  package com.puppycrawl.tools.checkstyle.checks.javadoc;
21  
22  import java.util.regex.Pattern;
23  
24  import com.puppycrawl.tools.checkstyle.StatelessCheck;
25  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
26  import com.puppycrawl.tools.checkstyle.api.DetailAST;
27  import com.puppycrawl.tools.checkstyle.api.FileContents;
28  import com.puppycrawl.tools.checkstyle.api.Scope;
29  import com.puppycrawl.tools.checkstyle.api.TextBlock;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
32  
33  /**
34   * Checks that a variable has Javadoc comment. Ignores {@code serialVersionUID} fields.
35   *
36   */
37  @StatelessCheck
38  public class JavadocVariableCheck
39      extends AbstractCheck {
40  
41      /**
42       * A key is pointing to the warning message text in "messages.properties"
43       * file.
44       */
45      public static final String MSG_JAVADOC_MISSING = "javadoc.missing";
46  
47      /** The scope to check. */
48      private Scope scope = Scope.PRIVATE;
49  
50      /** The visibility scope where Javadoc comments shouldn't be checked. **/
51      private Scope excludeScope;
52  
53      /** The pattern to ignore variable name. */
54      private Pattern ignoreNamePattern;
55  
56      /**
57       * Sets the scope to check.
58       * @param scope a scope.
59       */
60      public void setScope(Scope scope) {
61          this.scope = scope;
62      }
63  
64      /**
65       * Set the excludeScope.
66       * @param excludeScope a scope.
67       */
68      public void setExcludeScope(Scope excludeScope) {
69          this.excludeScope = excludeScope;
70      }
71  
72      /**
73       * Sets the variable names to ignore in the check.
74       * @param pattern a pattern.
75       */
76      public void setIgnoreNamePattern(Pattern pattern) {
77          ignoreNamePattern = pattern;
78      }
79  
80      @Override
81      public int[] getDefaultTokens() {
82          return getAcceptableTokens();
83      }
84  
85      @Override
86      public int[] getAcceptableTokens() {
87          return new int[] {
88              TokenTypes.VARIABLE_DEF,
89              TokenTypes.ENUM_CONSTANT_DEF,
90          };
91      }
92  
93      /*
94       * Skipping enum values is requested.
95       * Checkstyle's issue #1669: https://github.com/checkstyle/checkstyle/issues/1669
96       */
97      @Override
98      public int[] getRequiredTokens() {
99          return new int[] {
100             TokenTypes.VARIABLE_DEF,
101         };
102     }
103 
104     @Override
105     public void visitToken(DetailAST ast) {
106         if (shouldCheck(ast)) {
107             final FileContents contents = getFileContents();
108             final TextBlock textBlock =
109                 contents.getJavadocBefore(ast.getLineNo());
110 
111             if (textBlock == null) {
112                 log(ast, MSG_JAVADOC_MISSING);
113             }
114         }
115     }
116 
117     /**
118      * Decides whether the variable name of an AST is in the ignore list.
119      * @param ast the AST to check
120      * @return true if the variable name of ast is in the ignore list.
121      */
122     private boolean isIgnored(DetailAST ast) {
123         final String name = ast.findFirstToken(TokenTypes.IDENT).getText();
124         return ignoreNamePattern != null && ignoreNamePattern.matcher(name).matches()
125             || "serialVersionUID".equals(name);
126     }
127 
128     /**
129      * Whether we should check this node.
130      * @param ast a given node.
131      * @return whether we should check a given node.
132      */
133     private boolean shouldCheck(final DetailAST ast) {
134         boolean result = false;
135         if (!ScopeUtil.isInCodeBlock(ast) && !isIgnored(ast)) {
136             Scope customScope = Scope.PUBLIC;
137             if (ast.getType() != TokenTypes.ENUM_CONSTANT_DEF
138                     && !ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
139                 final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
140                 customScope = ScopeUtil.getScopeFromMods(mods);
141             }
142 
143             final Scope surroundingScope = ScopeUtil.getSurroundingScope(ast);
144             result = customScope.isIn(scope) && surroundingScope.isIn(scope)
145                 && (excludeScope == null
146                     || !customScope.isIn(excludeScope)
147                     || !surroundingScope.isIn(excludeScope));
148         }
149         return result;
150     }
151 
152 }