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 * <div>
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 * </div>
36 *
37 * <ul>
38 * <li>
39 * Property {@code ignoreInlineTags} - Control whether
40 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
41 * inline tags</a> must be ignored.
42 * Type is {@code boolean}.
43 * Default value is {@code true}.
44 * </li>
45 * <li>
46 * Property {@code ignoredTags} - Specify
47 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
48 * block tags</a> which are ignored by the check.
49 * Type is {@code java.lang.String[]}.
50 * Default value is {@code ""}.
51 * </li>
52 * <li>
53 * Property {@code violateExecutionOnNonTightHtml} - Control when to print violations
54 * if the Javadoc being examined by this check violates the tight html rules defined at
55 * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules">Tight-HTML Rules</a>.
56 * Type is {@code boolean}.
57 * Default value is {@code false}.
58 * </li>
59 * </ul>
60 *
61 * <p>
62 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
63 * </p>
64 *
65 * <p>
66 * Violation Message Keys:
67 * </p>
68 * <ul>
69 * <li>
70 * {@code javadoc.missed.html.close}
71 * </li>
72 * <li>
73 * {@code javadoc.parse.rule.error}
74 * </li>
75 * <li>
76 * {@code javadoc.unclosedHtml}
77 * </li>
78 * <li>
79 * {@code javadoc.wrong.singleton.html.tag}
80 * </li>
81 * <li>
82 * {@code singleline.javadoc}
83 * </li>
84 * </ul>
85 *
86 * @since 6.0
87 */
88 @StatelessCheck
89 public class SingleLineJavadocCheck extends AbstractJavadocCheck {
90
91 /**
92 * A key is pointing to the warning message text in "messages.properties"
93 * file.
94 */
95 public static final String MSG_KEY = "singleline.javadoc";
96
97 /**
98 * Specify
99 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
100 * block tags</a> which are ignored by the check.
101 */
102 private Set<String> ignoredTags = Set.of();
103
104 /**
105 * Control whether
106 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
107 * inline tags</a> must be ignored.
108 */
109 private boolean ignoreInlineTags = true;
110
111 /**
112 * Setter to specify
113 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
114 * block tags</a> which are ignored by the check.
115 *
116 * @param tags to be ignored by check.
117 * @since 6.8
118 */
119 public void setIgnoredTags(String... tags) {
120 ignoredTags = Set.of(tags);
121 }
122
123 /**
124 * Setter to control whether
125 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
126 * inline tags</a> must be ignored.
127 *
128 * @param ignoreInlineTags whether inline tags must be ignored.
129 * @since 6.8
130 */
131 public void setIgnoreInlineTags(boolean ignoreInlineTags) {
132 this.ignoreInlineTags = ignoreInlineTags;
133 }
134
135 @Override
136 public int[] getDefaultJavadocTokens() {
137 return new int[] {
138 JavadocTokenTypes.JAVADOC,
139 };
140 }
141
142 @Override
143 public int[] getRequiredJavadocTokens() {
144 return getAcceptableJavadocTokens();
145 }
146
147 @Override
148 public void visitJavadocToken(DetailNode ast) {
149 if (isSingleLineJavadoc(getBlockCommentAst())
150 && (hasJavadocTags(ast) || !ignoreInlineTags && hasJavadocInlineTags(ast))) {
151 log(ast.getLineNumber(), MSG_KEY);
152 }
153 }
154
155 /**
156 * Checks if comment is single-line comment.
157 *
158 * @param blockCommentStart the AST tree in which a block comment starts
159 * @return true, if comment is single-line comment.
160 */
161 private static boolean isSingleLineJavadoc(DetailAST blockCommentStart) {
162 final DetailAST blockCommentEnd = blockCommentStart.getLastChild();
163 return TokenUtil.areOnSameLine(blockCommentStart, blockCommentEnd);
164 }
165
166 /**
167 * Checks if comment has javadoc tags which are not ignored. Also works
168 * on custom tags. As block tags can be interpreted only at the beginning of a line,
169 * only the first instance is checked.
170 *
171 * @param javadocRoot javadoc root node.
172 * @return true, if comment has javadoc tags which are not ignored.
173 * @see <a href=
174 * "https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#blockandinlinetags">
175 * Block and inline tags</a>
176 */
177 private boolean hasJavadocTags(DetailNode javadocRoot) {
178 final DetailNode javadocTagSection =
179 JavadocUtil.findFirstToken(javadocRoot, JavadocTokenTypes.JAVADOC_TAG);
180 return javadocTagSection != null && !isTagIgnored(javadocTagSection);
181 }
182
183 /**
184 * Checks if comment has in-line tags which are not ignored.
185 *
186 * @param javadocRoot javadoc root node.
187 * @return true, if comment has in-line tags which are not ignored.
188 * @see <a href=
189 * "https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#javadoctags">
190 * JavadocTags</a>
191 */
192 private boolean hasJavadocInlineTags(DetailNode javadocRoot) {
193 DetailNode javadocTagSection =
194 JavadocUtil.findFirstToken(javadocRoot, JavadocTokenTypes.JAVADOC_INLINE_TAG);
195 boolean foundTag = false;
196 while (javadocTagSection != null) {
197 if (!isTagIgnored(javadocTagSection)) {
198 foundTag = true;
199 break;
200 }
201 javadocTagSection = JavadocUtil.getNextSibling(
202 javadocTagSection, JavadocTokenTypes.JAVADOC_INLINE_TAG);
203 }
204 return foundTag;
205 }
206
207 /**
208 * Checks if list of ignored tags contains javadocTagSection's javadoc tag.
209 *
210 * @param javadocTagSection to check javadoc tag in.
211 * @return true, if ignoredTags contains javadocTagSection's javadoc tag.
212 */
213 private boolean isTagIgnored(DetailNode javadocTagSection) {
214 return ignoredTags.contains(JavadocUtil.getTagName(javadocTagSection));
215 }
216
217 }