001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2016 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.CommonUtils;
031import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
032
033/**
034 * Checks that a variable has Javadoc comment. Ignores <code>serialVersionUID</code> fields.
035 *
036 * @author Oliver Burn
037 */
038public class JavadocVariableCheck
039    extends AbstractCheck {
040
041    /**
042     * A key is pointing to the warning message text in "messages.properties"
043     * file.
044     */
045    public static final String MSG_JAVADOC_MISSING = "javadoc.missing";
046
047    /** The scope to check. */
048    private Scope scope = Scope.PRIVATE;
049
050    /** The visibility scope where Javadoc comments shouldn't be checked. **/
051    private Scope excludeScope;
052
053    /** The pattern to ignore variable name. */
054    private Pattern ignoreNamePattern;
055
056    /**
057     * Sets the scope to check.
058     * @param from string to get the scope from
059     */
060    public void setScope(String from) {
061        scope = Scope.getInstance(from);
062    }
063
064    /**
065     * Set the excludeScope.
066     * @param excludeScope a {@code String} value
067     */
068    public void setExcludeScope(String excludeScope) {
069        this.excludeScope = Scope.getInstance(excludeScope);
070    }
071
072    /**
073     * Sets the variable names to ignore in the check.
074     * @param regexp regular expression to define variable names to ignore.
075     * @throws org.apache.commons.beanutils.ConversionException if unable to create Pattern object.
076     */
077    public void setIgnoreNamePattern(String regexp) {
078        ignoreNamePattern = CommonUtils.createPattern(regexp);
079    }
080
081    @Override
082    public int[] getDefaultTokens() {
083        return getAcceptableTokens();
084    }
085
086    @Override
087    public int[] getAcceptableTokens() {
088        return new int[] {
089            TokenTypes.VARIABLE_DEF,
090            TokenTypes.ENUM_CONSTANT_DEF,
091        };
092    }
093
094    /*
095     * Skipping enum values is requested.
096     * Checkstyle's issue #1669: https://github.com/checkstyle/checkstyle/issues/1669
097     */
098    @Override
099    public int[] getRequiredTokens() {
100        return new int[] {
101            TokenTypes.VARIABLE_DEF,
102        };
103    }
104
105    @Override
106    public void visitToken(DetailAST ast) {
107        if (shouldCheck(ast)) {
108            final FileContents contents = getFileContents();
109            final TextBlock textBlock =
110                contents.getJavadocBefore(ast.getLineNo());
111
112            if (textBlock == null) {
113                log(ast, MSG_JAVADOC_MISSING);
114            }
115        }
116    }
117
118    /**
119     * Decides whether the variable name of an AST is in the ignore list.
120     * @param ast the AST to check
121     * @return true if the variable name of ast is in the ignore list.
122     */
123    private boolean isIgnored(DetailAST ast) {
124        final String name = ast.findFirstToken(TokenTypes.IDENT).getText();
125        return ignoreNamePattern != null && ignoreNamePattern.matcher(name).matches()
126            || "serialVersionUID".equals(name);
127    }
128
129    /**
130     * Whether we should check this node.
131     * @param ast a given node.
132     * @return whether we should check a given node.
133     */
134    private boolean shouldCheck(final DetailAST ast) {
135        if (ScopeUtils.isInCodeBlock(ast) || isIgnored(ast)) {
136            return false;
137        }
138
139        final Scope customScope;
140        if (ast.getType() == TokenTypes.ENUM_CONSTANT_DEF) {
141            customScope = Scope.PUBLIC;
142        }
143        else {
144            final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
145            final Scope declaredScope = ScopeUtils.getScopeFromMods(mods);
146
147            if (ScopeUtils.isInInterfaceOrAnnotationBlock(ast)) {
148                customScope = Scope.PUBLIC;
149            }
150            else {
151                customScope = declaredScope;
152            }
153        }
154
155        final Scope surroundingScope = ScopeUtils.getSurroundingScope(ast);
156
157        return customScope.isIn(scope) && surroundingScope.isIn(scope)
158            && (excludeScope == null
159                || !customScope.isIn(excludeScope)
160                || !surroundingScope.isIn(excludeScope));
161    }
162}