001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.javadoc;
021
022import java.util.regex.Pattern;
023
024import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
025import com.puppycrawl.tools.checkstyle.api.DetailAST;
026import com.puppycrawl.tools.checkstyle.api.FileContents;
027import com.puppycrawl.tools.checkstyle.api.Scope;
028import com.puppycrawl.tools.checkstyle.api.TextBlock;
029import com.puppycrawl.tools.checkstyle.api.TokenTypes;
030import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
031
032/**
033 * Checks that a variable has Javadoc comment. Ignores <code>serialVersionUID</code> fields.
034 *
035 * @author Oliver Burn
036 */
037public class JavadocVariableCheck
038    extends AbstractCheck {
039
040    /**
041     * A key is pointing to the warning message text in "messages.properties"
042     * file.
043     */
044    public static final String MSG_JAVADOC_MISSING = "javadoc.missing";
045
046    /** The scope to check. */
047    private Scope scope = Scope.PRIVATE;
048
049    /** The visibility scope where Javadoc comments shouldn't be checked. **/
050    private Scope excludeScope;
051
052    /** The pattern to ignore variable name. */
053    private Pattern ignoreNamePattern;
054
055    /**
056     * Sets the scope to check.
057     * @param scope a scope.
058     */
059    public void setScope(Scope scope) {
060        this.scope = scope;
061    }
062
063    /**
064     * Set the excludeScope.
065     * @param excludeScope a scope.
066     */
067    public void setExcludeScope(Scope excludeScope) {
068        this.excludeScope = excludeScope;
069    }
070
071    /**
072     * Sets the variable names to ignore in the check.
073     * @param pattern a pattern.
074     */
075    public void setIgnoreNamePattern(Pattern pattern) {
076        ignoreNamePattern = pattern;
077    }
078
079    @Override
080    public int[] getDefaultTokens() {
081        return getAcceptableTokens();
082    }
083
084    @Override
085    public int[] getAcceptableTokens() {
086        return new int[] {
087            TokenTypes.VARIABLE_DEF,
088            TokenTypes.ENUM_CONSTANT_DEF,
089        };
090    }
091
092    /*
093     * Skipping enum values is requested.
094     * Checkstyle's issue #1669: https://github.com/checkstyle/checkstyle/issues/1669
095     */
096    @Override
097    public int[] getRequiredTokens() {
098        return new int[] {
099            TokenTypes.VARIABLE_DEF,
100        };
101    }
102
103    @Override
104    public void visitToken(DetailAST ast) {
105        if (shouldCheck(ast)) {
106            final FileContents contents = getFileContents();
107            final TextBlock textBlock =
108                contents.getJavadocBefore(ast.getLineNo());
109
110            if (textBlock == null) {
111                log(ast, MSG_JAVADOC_MISSING);
112            }
113        }
114    }
115
116    /**
117     * Decides whether the variable name of an AST is in the ignore list.
118     * @param ast the AST to check
119     * @return true if the variable name of ast is in the ignore list.
120     */
121    private boolean isIgnored(DetailAST ast) {
122        final String name = ast.findFirstToken(TokenTypes.IDENT).getText();
123        return ignoreNamePattern != null && ignoreNamePattern.matcher(name).matches()
124            || "serialVersionUID".equals(name);
125    }
126
127    /**
128     * Whether we should check this node.
129     * @param ast a given node.
130     * @return whether we should check a given node.
131     */
132    private boolean shouldCheck(final DetailAST ast) {
133        boolean result = false;
134        if (!ScopeUtils.isInCodeBlock(ast) && !isIgnored(ast)) {
135            Scope customScope = Scope.PUBLIC;
136            if (ast.getType() != TokenTypes.ENUM_CONSTANT_DEF
137                    && !ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) {
138                final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
139                customScope = ScopeUtils.getScopeFromMods(mods);
140            }
141
142            final Scope surroundingScope = ScopeUtils.getSurroundingScope(ast);
143            result = customScope.isIn(scope) && surroundingScope.isIn(scope)
144                && (excludeScope == null
145                    || !customScope.isIn(excludeScope)
146                    || !surroundingScope.isIn(excludeScope));
147        }
148        return result;
149    }
150}