001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2024 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.StatelessCheck;
025import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
026import com.puppycrawl.tools.checkstyle.api.DetailAST;
027import com.puppycrawl.tools.checkstyle.api.FileContents;
028import com.puppycrawl.tools.checkstyle.api.Scope;
029import com.puppycrawl.tools.checkstyle.api.TextBlock;
030import com.puppycrawl.tools.checkstyle.api.TokenTypes;
031import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
032
033/**
034 * <p>
035 * Checks that a variable has a Javadoc comment. Ignores {@code serialVersionUID} fields.
036 * </p>
037 * <ul>
038 * <li>
039 * Property {@code excludeScope} - Specify the visibility scope where Javadoc
040 * comments are not checked.
041 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}.
042 * Default value is {@code null}.
043 * </li>
044 * <li>
045 * Property {@code ignoreNamePattern} - Specify the regexp to define variable names to ignore.
046 * Type is {@code java.util.regex.Pattern}.
047 * Default value is {@code null}.
048 * </li>
049 * <li>
050 * Property {@code scope} - Specify the visibility scope where Javadoc comments are checked.
051 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}.
052 * Default value is {@code private}.
053 * </li>
054 * <li>
055 * Property {@code tokens} - tokens to check
056 * Type is {@code java.lang.String[]}.
057 * Validation type is {@code tokenSet}.
058 * Default value is:
059 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">
060 * ENUM_CONSTANT_DEF</a>.
061 * </li>
062 * </ul>
063 * <p>
064 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
065 * </p>
066 * <p>
067 * Violation Message Keys:
068 * </p>
069 * <ul>
070 * <li>
071 * {@code javadoc.missing}
072 * </li>
073 * </ul>
074 *
075 * @since 3.0
076 */
077@StatelessCheck
078public class JavadocVariableCheck
079    extends AbstractCheck {
080
081    /**
082     * A key is pointing to the warning message text in "messages.properties"
083     * file.
084     */
085    public static final String MSG_JAVADOC_MISSING = "javadoc.missing";
086
087    /** Specify the visibility scope where Javadoc comments are checked. */
088    private Scope scope = Scope.PRIVATE;
089
090    /** Specify the visibility scope where Javadoc comments are not checked. */
091    private Scope excludeScope;
092
093    /** Specify the regexp to define variable names to ignore. */
094    private Pattern ignoreNamePattern;
095
096    /**
097     * Setter to specify the visibility scope where Javadoc comments are checked.
098     *
099     * @param scope a scope.
100     * @since 3.0
101     */
102    public void setScope(Scope scope) {
103        this.scope = scope;
104    }
105
106    /**
107     * Setter to specify the visibility scope where Javadoc comments are not checked.
108     *
109     * @param excludeScope a scope.
110     * @since 3.4
111     */
112    public void setExcludeScope(Scope excludeScope) {
113        this.excludeScope = excludeScope;
114    }
115
116    /**
117     * Setter to specify the regexp to define variable names to ignore.
118     *
119     * @param pattern a pattern.
120     * @since 5.8
121     */
122    public void setIgnoreNamePattern(Pattern pattern) {
123        ignoreNamePattern = pattern;
124    }
125
126    @Override
127    public int[] getDefaultTokens() {
128        return getAcceptableTokens();
129    }
130
131    @Override
132    public int[] getAcceptableTokens() {
133        return new int[] {
134            TokenTypes.VARIABLE_DEF,
135            TokenTypes.ENUM_CONSTANT_DEF,
136        };
137    }
138
139    /*
140     * Skipping enum values is requested.
141     * Checkstyle's issue #1669: https://github.com/checkstyle/checkstyle/issues/1669
142     */
143    @Override
144    public int[] getRequiredTokens() {
145        return new int[] {
146            TokenTypes.VARIABLE_DEF,
147        };
148    }
149
150    // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166
151    @SuppressWarnings("deprecation")
152    @Override
153    public void visitToken(DetailAST ast) {
154        if (shouldCheck(ast)) {
155            final FileContents contents = getFileContents();
156            final TextBlock textBlock =
157                contents.getJavadocBefore(ast.getLineNo());
158
159            if (textBlock == null) {
160                log(ast, MSG_JAVADOC_MISSING);
161            }
162        }
163    }
164
165    /**
166     * Decides whether the variable name of an AST is in the ignore list.
167     *
168     * @param ast the AST to check
169     * @return true if the variable name of ast is in the ignore list.
170     */
171    private boolean isIgnored(DetailAST ast) {
172        final String name = ast.findFirstToken(TokenTypes.IDENT).getText();
173        return ignoreNamePattern != null && ignoreNamePattern.matcher(name).matches()
174            || "serialVersionUID".equals(name);
175    }
176
177    /**
178     * Whether we should check this node.
179     *
180     * @param ast a given node.
181     * @return whether we should check a given node.
182     */
183    private boolean shouldCheck(final DetailAST ast) {
184        boolean result = false;
185        if (!ScopeUtil.isInCodeBlock(ast) && !isIgnored(ast)) {
186            final Scope customScope = ScopeUtil.getScope(ast);
187            final Scope surroundingScope = ScopeUtil.getSurroundingScope(ast);
188            result = customScope.isIn(scope) && surroundingScope.isIn(scope)
189                && (excludeScope == null
190                    || !customScope.isIn(excludeScope)
191                    || !surroundingScope.isIn(excludeScope));
192        }
193        return result;
194    }
195
196}