1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 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.JavadocCommentsTokenTypes;
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 * @since 6.0
38 */
39 @StatelessCheck
40 public class SingleLineJavadocCheck extends AbstractJavadocCheck {
41
42 /**
43 * A key is pointing to the warning message text in "messages.properties"
44 * file.
45 */
46 public static final String MSG_KEY = "singleline.javadoc";
47
48 /**
49 * Specify
50 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
51 * block tags</a> which are ignored by the check.
52 */
53 private Set<String> ignoredTags = Set.of();
54
55 /**
56 * Control whether
57 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
58 * inline tags</a> must be ignored.
59 */
60 private boolean ignoreInlineTags = true;
61
62 /**
63 * Setter to specify
64 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
65 * block tags</a> which are ignored by the check.
66 *
67 * @param tags to be ignored by check.
68 * @since 6.8
69 */
70 public void setIgnoredTags(String... tags) {
71 ignoredTags = Set.of(tags);
72 }
73
74 /**
75 * Setter to control whether
76 * <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDBEFIF">
77 * inline tags</a> must be ignored.
78 *
79 * @param ignoreInlineTags whether inline tags must be ignored.
80 * @since 6.8
81 */
82 public void setIgnoreInlineTags(boolean ignoreInlineTags) {
83 this.ignoreInlineTags = ignoreInlineTags;
84 }
85
86 @Override
87 public int[] getDefaultJavadocTokens() {
88 return new int[] {
89 JavadocCommentsTokenTypes.JAVADOC_CONTENT,
90 };
91 }
92
93 @Override
94 public int[] getRequiredJavadocTokens() {
95 return getAcceptableJavadocTokens();
96 }
97
98 @Override
99 public void visitJavadocToken(DetailNode ast) {
100 if (isSingleLineJavadoc(getBlockCommentAst())
101 && (hasJavadocTags(ast) || !ignoreInlineTags && hasJavadocInlineTags(ast))) {
102 log(ast.getLineNumber(), MSG_KEY);
103 }
104 }
105
106 /**
107 * Checks if comment is single-line comment.
108 *
109 * @param blockCommentStart the AST tree in which a block comment starts
110 * @return true, if comment is single-line comment.
111 */
112 private static boolean isSingleLineJavadoc(DetailAST blockCommentStart) {
113 final DetailAST blockCommentEnd = blockCommentStart.getLastChild();
114 return TokenUtil.areOnSameLine(blockCommentStart, blockCommentEnd);
115 }
116
117 /**
118 * Checks if comment has javadoc tags which are not ignored. Also works
119 * on custom tags. As block tags can be interpreted only at the beginning of a line,
120 * only the first instance is checked.
121 *
122 * @param javadocRoot javadoc root node.
123 * @return true, if comment has javadoc tags which are not ignored.
124 * @see <a href=
125 * "https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#blockandinlinetags">
126 * Block and inline tags</a>
127 */
128 private boolean hasJavadocTags(DetailNode javadocRoot) {
129 final DetailNode javadocTagSection =
130 JavadocUtil.findFirstToken(
131 javadocRoot, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG);
132 return javadocTagSection != null && !isTagIgnored(javadocTagSection);
133 }
134
135 /**
136 * Checks if comment has in-line tags which are not ignored.
137 *
138 * @param javadocRoot javadoc root node.
139 * @return true, if comment has in-line tags which are not ignored.
140 * @see <a href=
141 * "https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#javadoctags">
142 * JavadocTags</a>
143 */
144 private boolean hasJavadocInlineTags(DetailNode javadocRoot) {
145 DetailNode javadocTagSection =
146 JavadocUtil.findFirstToken(
147 javadocRoot, JavadocCommentsTokenTypes.JAVADOC_INLINE_TAG);
148 boolean foundTag = false;
149 while (javadocTagSection != null) {
150 if (!isTagIgnored(javadocTagSection)) {
151 foundTag = true;
152 break;
153 }
154 javadocTagSection = JavadocUtil.getNextSibling(
155 javadocTagSection, JavadocCommentsTokenTypes.JAVADOC_INLINE_TAG);
156 }
157 return foundTag;
158 }
159
160 /**
161 * Checks if list of ignored tags contains javadocTagSection's javadoc tag.
162 *
163 * @param javadocTagSection to check javadoc tag in.
164 * @return true, if ignoredTags contains javadocTagSection's javadoc tag.
165 */
166 private boolean isTagIgnored(DetailNode javadocTagSection) {
167 final String tagName = JavadocUtil.getTagName(javadocTagSection);
168 return ignoredTags.contains("@" + tagName);
169 }
170
171 }