1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2024 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.checks.javadoc;
21
22 import java.util.Set;
23
24 import com.puppycrawl.tools.checkstyle.StatelessCheck;
25 import com.puppycrawl.tools.checkstyle.api.DetailAST;
26 import com.puppycrawl.tools.checkstyle.api.DetailNode;
27 import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
28 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
29 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
30
31 /**
32 * <p>
33 * Checks that a Javadoc block can fit in a single-line and doesn't contain block tags.
34 * Javadoc comment that contains at least one block tag should be formatted in a few lines.
35 * </p>
36 * <ul>
37 * <li>
38 * Property {@code ignoreInlineTags} - Control whether
39 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
40 * inline tags</a> must be ignored.
41 * Type is {@code boolean}.
42 * Default value is {@code true}.
43 * </li>
44 * <li>
45 * Property {@code ignoredTags} - Specify
46 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
47 * block tags</a> which are ignored by the check.
48 * Type is {@code java.lang.String[]}.
49 * Default value is {@code ""}.
50 * </li>
51 * <li>
52 * Property {@code violateExecutionOnNonTightHtml} - Control when to print violations
53 * if the Javadoc being examined by this check violates the tight html rules defined at
54 * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules">Tight-HTML Rules</a>.
55 * Type is {@code boolean}.
56 * Default value is {@code false}.
57 * </li>
58 * </ul>
59 * <p>
60 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
61 * </p>
62 * <p>
63 * Violation Message Keys:
64 * </p>
65 * <ul>
66 * <li>
67 * {@code javadoc.missed.html.close}
68 * </li>
69 * <li>
70 * {@code javadoc.parse.rule.error}
71 * </li>
72 * <li>
73 * {@code javadoc.unclosedHtml}
74 * </li>
75 * <li>
76 * {@code javadoc.wrong.singleton.html.tag}
77 * </li>
78 * <li>
79 * {@code singleline.javadoc}
80 * </li>
81 * </ul>
82 *
83 * @since 6.0
84 */
85 @StatelessCheck
86 public class SingleLineJavadocCheck extends AbstractJavadocCheck {
87
88 /**
89 * A key is pointing to the warning message text in "messages.properties"
90 * file.
91 */
92 public static final String MSG_KEY = "singleline.javadoc";
93
94 /**
95 * Specify
96 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
97 * block tags</a> which are ignored by the check.
98 */
99 private Set<String> ignoredTags = Set.of();
100
101 /**
102 * Control whether
103 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
104 * inline tags</a> must be ignored.
105 */
106 private boolean ignoreInlineTags = true;
107
108 /**
109 * Setter to specify
110 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
111 * block tags</a> which are ignored by the check.
112 *
113 * @param tags to be ignored by check.
114 * @since 6.8
115 */
116 public void setIgnoredTags(String... tags) {
117 ignoredTags = Set.of(tags);
118 }
119
120 /**
121 * Setter to control whether
122 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
123 * inline tags</a> must be ignored.
124 *
125 * @param ignoreInlineTags whether inline tags must be ignored.
126 * @since 6.8
127 */
128 public void setIgnoreInlineTags(boolean ignoreInlineTags) {
129 this.ignoreInlineTags = ignoreInlineTags;
130 }
131
132 @Override
133 public int[] getDefaultJavadocTokens() {
134 return new int[] {
135 JavadocTokenTypes.JAVADOC,
136 };
137 }
138
139 @Override
140 public int[] getRequiredJavadocTokens() {
141 return getAcceptableJavadocTokens();
142 }
143
144 @Override
145 public void visitJavadocToken(DetailNode ast) {
146 if (isSingleLineJavadoc(getBlockCommentAst())
147 && (hasJavadocTags(ast) || !ignoreInlineTags && hasJavadocInlineTags(ast))) {
148 log(ast.getLineNumber(), MSG_KEY);
149 }
150 }
151
152 /**
153 * Checks if comment is single-line comment.
154 *
155 * @param blockCommentStart the AST tree in which a block comment starts
156 * @return true, if comment is single-line comment.
157 */
158 private static boolean isSingleLineJavadoc(DetailAST blockCommentStart) {
159 final DetailAST blockCommentEnd = blockCommentStart.getLastChild();
160 return TokenUtil.areOnSameLine(blockCommentStart, blockCommentEnd);
161 }
162
163 /**
164 * Checks if comment has javadoc tags which are not ignored. Also works
165 * on custom tags. As block tags can be interpreted only at the beginning of a line,
166 * only the first instance is checked.
167 *
168 * @param javadocRoot javadoc root node.
169 * @return true, if comment has javadoc tags which are not ignored.
170 * @see <a href=
171 * "https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#blockandinlinetags">
172 * Block and inline tags</a>
173 */
174 private boolean hasJavadocTags(DetailNode javadocRoot) {
175 final DetailNode javadocTagSection =
176 JavadocUtil.findFirstToken(javadocRoot, JavadocTokenTypes.JAVADOC_TAG);
177 return javadocTagSection != null && !isTagIgnored(javadocTagSection);
178 }
179
180 /**
181 * Checks if comment has in-line tags which are not ignored.
182 *
183 * @param javadocRoot javadoc root node.
184 * @return true, if comment has in-line tags which are not ignored.
185 * @see <a href=
186 * "https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#javadoctags">
187 * JavadocTags</a>
188 */
189 private boolean hasJavadocInlineTags(DetailNode javadocRoot) {
190 DetailNode javadocTagSection =
191 JavadocUtil.findFirstToken(javadocRoot, JavadocTokenTypes.JAVADOC_INLINE_TAG);
192 boolean foundTag = false;
193 while (javadocTagSection != null) {
194 if (!isTagIgnored(javadocTagSection)) {
195 foundTag = true;
196 break;
197 }
198 javadocTagSection = JavadocUtil.getNextSibling(
199 javadocTagSection, JavadocTokenTypes.JAVADOC_INLINE_TAG);
200 }
201 return foundTag;
202 }
203
204 /**
205 * Checks if list of ignored tags contains javadocTagSection's javadoc tag.
206 *
207 * @param javadocTagSection to check javadoc tag in.
208 * @return true, if ignoredTags contains javadocTagSection's javadoc tag.
209 */
210 private boolean isTagIgnored(DetailNode javadocTagSection) {
211 return ignoredTags.contains(JavadocUtil.getTagName(javadocTagSection));
212 }
213
214 }