001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2022 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 com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.DetailNode;
024import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
025import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
026import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
027
028/**
029 * <p>
030 * Checks that the block tag is followed by description.
031 * </p>
032 * <ul>
033 * <li>
034 * Property {@code violateExecutionOnNonTightHtml} - Control when to print violations
035 * if the Javadoc being examined by this check violates the tight html rules defined at
036 * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules">Tight-HTML Rules</a>.
037 * Type is {@code boolean}.
038 * Default value is {@code false}.
039 * </li>
040 * <li>
041 * Property {@code javadocTokens} - javadoc tokens to check
042 * Type is {@code java.lang.String[]}.
043 * Validation type is {@code tokenSet}.
044 * Default value is
045 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#PARAM_LITERAL">
046 * PARAM_LITERAL</a>,
047 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#RETURN_LITERAL">
048 * RETURN_LITERAL</a>,
049 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#THROWS_LITERAL">
050 * THROWS_LITERAL</a>,
051 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#EXCEPTION_LITERAL">
052 * EXCEPTION_LITERAL</a>,
053 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/JavadocTokenTypes.html#DEPRECATED_LITERAL">
054 * DEPRECATED_LITERAL</a>.
055 * </li>
056 * </ul>
057 * <p>
058 * To configure the default check that will check {@code @param}, {@code @return},
059 * {@code @throws}, {@code @deprecated}:
060 * </p>
061 * <pre>
062 * &lt;module name="NonEmptyAtclauseDescription"/&gt;
063 * </pre>
064 * <p>
065 * Example:
066 * </p>
067 * <pre>
068 * class Test
069 * {
070 * &#47;**
071 * * Violation for param "b" and at tags "deprecated", "throws".
072 * * &#64;param a Some javadoc // OK
073 * * &#64;param b
074 * * &#64;deprecated
075 * * &#64;throws Exception
076 * *&#47;
077 * public int method(String a, int b) throws Exception
078 * {
079 * return 1;
080 * }
081 * }
082 * </pre>
083 * <p>
084 * To configure the check to validate only {@code @param} and {@code @return} tags:
085 * </p>
086 * <pre>
087 * &lt;module name="NonEmptyAtclauseDescription"&gt;
088 *   &lt;property name="javadocTokens" value="PARAM_LITERAL,RETURN_LITERAL"/&gt;
089 * &lt;/module&gt;
090 * </pre>
091 * <p>
092 * Example:
093 * </p>
094 * <pre>
095 * class Test
096 * {
097 * &#47;**
098 * * Violation for param "b". Tags "deprecated", "throws" are ignored.
099 * * &#64;param a Some javadoc // OK
100 * * &#64;param b
101 * * &#64;deprecated
102 * * &#64;throws Exception
103 * *&#47;
104 * public int method(String a, int b) throws Exception
105 * {
106 * return 1;
107 * }
108 * }
109 * </pre>
110 * <p>
111 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
112 * </p>
113 * <p>
114 * Violation Message Keys:
115 * </p>
116 * <ul>
117 * <li>
118 * {@code javadoc.missed.html.close}
119 * </li>
120 * <li>
121 * {@code javadoc.parse.rule.error}
122 * </li>
123 * <li>
124 * {@code javadoc.wrong.singleton.html.tag}
125 * </li>
126 * <li>
127 * {@code non.empty.atclause}
128 * </li>
129 * </ul>
130 *
131 * @since 6.0
132 */
133@StatelessCheck
134public class NonEmptyAtclauseDescriptionCheck extends AbstractJavadocCheck {
135
136    /**
137     * A key is pointing to the warning message text in "messages.properties"
138     * file.
139     */
140    public static final String MSG_KEY = "non.empty.atclause";
141
142    @Override
143    public int[] getDefaultJavadocTokens() {
144        return new int[] {
145            JavadocTokenTypes.PARAM_LITERAL,
146            JavadocTokenTypes.RETURN_LITERAL,
147            JavadocTokenTypes.THROWS_LITERAL,
148            JavadocTokenTypes.EXCEPTION_LITERAL,
149            JavadocTokenTypes.DEPRECATED_LITERAL,
150        };
151    }
152
153    @Override
154    public void visitJavadocToken(DetailNode ast) {
155        if (isEmptyTag(ast.getParent())) {
156            log(ast.getLineNumber(), MSG_KEY, ast.getText());
157        }
158    }
159
160    /**
161     * Tests if block tag is empty.
162     *
163     * @param tagNode block tag.
164     * @return true, if block tag is empty.
165     */
166    private static boolean isEmptyTag(DetailNode tagNode) {
167        final DetailNode tagDescription =
168                JavadocUtil.findFirstToken(tagNode, JavadocTokenTypes.DESCRIPTION);
169        return tagDescription == null
170            || hasOnlyEmptyText(tagDescription);
171    }
172
173    /**
174     * Tests if description node is empty (has only new lines and blank strings).
175     *
176     * @param description description node.
177     * @return true, if description node has only new lines and blank strings.
178     */
179    private static boolean hasOnlyEmptyText(DetailNode description) {
180        boolean result = true;
181        for (DetailNode child : description.getChildren()) {
182            if (child.getType() != JavadocTokenTypes.TEXT
183                    || !CommonUtil.isBlank(child.getText())) {
184                result = false;
185                break;
186            }
187        }
188        return result;
189    }
190
191}