001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2019 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.Arrays;
023import java.util.Collections;
024import java.util.List;
025
026import com.puppycrawl.tools.checkstyle.StatelessCheck;
027import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.api.FileContents;
030import com.puppycrawl.tools.checkstyle.api.Scope;
031import com.puppycrawl.tools.checkstyle.api.TextBlock;
032import com.puppycrawl.tools.checkstyle.api.TokenTypes;
033import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
034import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
035import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
036
037/**
038 * <p>
039 * Checks for missing Javadoc comments for class, enum, interface, and annotation interface
040 * definitions. The scope to verify is specified using the {@code Scope} class and defaults
041 * to {@code Scope.PUBLIC}. To verify another scope, set property scope to one of the
042 * {@code Scope} constants.
043 * </p>
044 * <ul>
045 * <li>
046 * Property {@code scope} - specify the visibility scope where Javadoc comments are checked.
047 * Default value is {@code public}.
048 * </li>
049 * <li>
050 * Property {@code excludeScope} - specify the visibility scope where Javadoc comments are not
051 * checked. Default value is {@code null}.
052 * </li>
053 * <li>
054 * Property {@code skipAnnotations} - specify the list of annotations that allow missed
055 * documentation. Only short names are allowed, e.g. {@code Generated}. Default value is
056 * {@code Generated}.
057 * </li>
058 * <li>
059 * Property {@code tokens} - tokens to check Default value is:
060 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
061 * INTERFACE_DEF</a>,
062 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
063 * CLASS_DEF</a>,
064 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
065 * ENUM_DEF</a>,
066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF">
067 * ANNOTATION_DEF</a>.
068 * </li>
069 * </ul>
070 * <p>
071 * To configure the default check to make sure all public class, enum, interface, and annotation
072 * interface, definitions have javadocs:
073 * </p>
074 * <pre>
075 * &lt;module name="MissingJavadocType"/&gt;
076 * </pre>
077 * <p>
078 * Example:
079 * </p>
080 * <pre>
081 * public class PublicClass {} // violation
082 * private class PublicClass {}
083 * protected class PublicClass {}
084 * class PackagePrivateClass {}
085 * </pre>
086 * <p>
087 * To configure the check for {@code private} scope:
088 * </p>
089 * <pre>
090 * &lt;module name="MissingJavadocType"&gt;
091 *   &lt;property name="scope" value="private"/&gt;
092 * &lt;/module&gt;
093 * </pre>
094 * <p>
095 * Example:
096 * </p>
097 * <pre>
098 * public class PublicClass {} // violation
099 * private class PublicClass {} // violation
100 * protected class PublicClass {} // violation
101 * class PackagePrivateClass {} // violation
102 * </pre>
103 * <p>
104 * To configure the check for {@code private} classes only:
105 * </p>
106 * <pre>
107 * &lt;module name="MissingJavadocType"&gt;
108 *   &lt;property name="scope" value="private"/&gt;
109 *   &lt;property name="excludeScope" value="package"/&gt;
110 * &lt;/module&gt;
111 * </pre>
112 * <p>
113 * Example:
114 * </p>
115 * <pre>
116 * public class PublicClass {}
117 * private class PublicClass {} // violation
118 * protected class PublicClass {}
119 * class PackagePrivateClass {}
120 * </pre>
121 * <p>
122 * Example that allows missing comments for classes annotated with {@code @SpringBootApplication}
123 * and {@code @Configuration}:
124 * </p>
125 * <pre>
126 * &#64;SpringBootApplication // no violations about missing comment on class
127 * public class Application {}
128 *
129 * &#64;Configuration // no violations about missing comment on class
130 * class DatabaseConfiguration {}
131 * </pre>
132 * <p>
133 * Use following configuration:
134 * </p>
135 * <pre>
136 * &lt;module name="MissingJavadocType"&gt;
137 *   &lt;property name="skipAnnotations" value="SpringBootApplication,Configuration"/&gt;
138 * &lt;/module&gt;
139 * </pre>
140 * @since 8.20
141 */
142@StatelessCheck
143public class MissingJavadocTypeCheck extends AbstractCheck {
144
145    /**
146     * A key is pointing to the warning message text in "messages.properties"
147     * file.
148     */
149    public static final String MSG_JAVADOC_MISSING = "javadoc.missing";
150
151    /** Specify the visibility scope where Javadoc comments are checked. */
152    private Scope scope = Scope.PUBLIC;
153    /** Specify the visibility scope where Javadoc comments are not checked. */
154    private Scope excludeScope;
155
156    /**
157     * Specify the list of annotations that allow missed documentation.
158     * Only short names are allowed, e.g. {@code Generated}.
159     */
160    private List<String> skipAnnotations = Collections.singletonList("Generated");
161
162    /**
163     * Setter to specify the visibility scope where Javadoc comments are checked.
164     * @param scope a scope.
165     */
166    public void setScope(Scope scope) {
167        this.scope = scope;
168    }
169
170    /**
171     * Setter to specify the visibility scope where Javadoc comments are not checked.
172     * @param excludeScope a scope.
173     */
174    public void setExcludeScope(Scope excludeScope) {
175        this.excludeScope = excludeScope;
176    }
177
178    /**
179     * Setter to specify the list of annotations that allow missed documentation.
180     * Only short names are allowed, e.g. {@code Generated}.
181     * @param userAnnotations user's value.
182     */
183    public void setSkipAnnotations(String... userAnnotations) {
184        skipAnnotations = Arrays.asList(userAnnotations);
185    }
186
187    @Override
188    public int[] getDefaultTokens() {
189        return getAcceptableTokens();
190    }
191
192    @Override
193    public int[] getAcceptableTokens() {
194        return new int[] {
195            TokenTypes.INTERFACE_DEF,
196            TokenTypes.CLASS_DEF,
197            TokenTypes.ENUM_DEF,
198            TokenTypes.ANNOTATION_DEF,
199        };
200    }
201
202    @Override
203    public int[] getRequiredTokens() {
204        return CommonUtil.EMPTY_INT_ARRAY;
205    }
206
207    @Override
208    public void visitToken(DetailAST ast) {
209        if (shouldCheck(ast)) {
210            final FileContents contents = getFileContents();
211            final int lineNo = ast.getLineNo();
212            final TextBlock textBlock = contents.getJavadocBefore(lineNo);
213            if (textBlock == null) {
214                log(lineNo, MSG_JAVADOC_MISSING);
215            }
216        }
217    }
218
219    /**
220     * Whether we should check this node.
221     * @param ast a given node.
222     * @return whether we should check a given node.
223     */
224    private boolean shouldCheck(final DetailAST ast) {
225        final Scope customScope;
226
227        if (ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
228            customScope = Scope.PUBLIC;
229        }
230        else {
231            final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
232            customScope = ScopeUtil.getScopeFromMods(mods);
233        }
234        final Scope surroundingScope = ScopeUtil.getSurroundingScope(ast);
235
236        return customScope.isIn(scope)
237            && (surroundingScope == null || surroundingScope.isIn(scope))
238            && (excludeScope == null
239                || !customScope.isIn(excludeScope)
240                || surroundingScope != null
241                && !surroundingScope.isIn(excludeScope))
242            && !AnnotationUtil.containsAnnotation(ast, skipAnnotations);
243    }
244
245}