View Javadoc
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 }